diff --git a/src/app/debugger/remix-debugger/README.md b/src/app/debugger/remix-debugger/README.md
new file mode 100644
index 0000000000..99745fc323
--- /dev/null
+++ b/src/app/debugger/remix-debugger/README.md
@@ -0,0 +1,73 @@
+# `remix-debugger`
+
+The Remix Debugger is a webapp to debug the Ethereum VM and transactions.
+
++ [Installation](#installation)
++ [Development](#development)
++ [First steps](#firststeps)
++ [Tests](#tests)
+
+## Installation
+
+Make sure Node is [installed on your setup](https://docs.npmjs.com/getting-started/installing-node), and that a [local `geth`/`eth` node is running](../README.md#how-to-use).
+
+```bash
+git clone https://github.com/ethereum/remix
+cd remix/remix-debugger
+npm install
+```
+
+This will build the debugger. Start it by opening `index.html` in your browser.
+
+## Development
+
+Run `npm run start_dev` to start a local webserver, accessible at `http://127.0.0.1:8080`. Your browser will reload when files are updated.
+
+## First steps
+
+Once Remix is connected to a node, you will be able to debug transactions.
+
+You can do that:
+ - using a block number and a transaction index.
+ - using a transaction hash.
+
+After loading the transaction succeeded, the hash, from and to field will show up. The VM trace is then loaded.
+
+The debugger itself contains several controls that allow stepping over the trace and seing the current state of a selected step:
+
+#### Slider and Stepping action
+
+The slider allows to move quickly from a state to another.
+
+Stepping actions are:
+- Step Into Back
+- Step Over Back
+- Step Over Forward
+- Step Into Forward
+- Jump Next Call: this will select the next state that refers to a context changes - CALL, CALLCODE, DELEGATECALL, CREATE.
+
+#### State Viewer
+
+The upper right panel contains basic informations about the current step:
+- VMTraceStep: the index in the trace of the current step.
+- Step
+- Add memory
+- Gas: gas used by this step
+- Remaining gas: gas left
+- Loaded address: the current code loaded, refers to the executing code.
+
+The other 6 panels describe the current selected state:
+ - Instructions list: list of all the instruction that defines the current executing code.
+ - Stack
+ - Storage Changes
+ - Memory
+ - Call Data$
+ - Call Stack
+
+## Tests
+
+* To run unit tests, run `npm test`.
+
+* For local headless browser tests:
+ * To install `selenium`: `npm run selenium-install`
+ * Every time you want to run local browser tests, run: `npm run test-browser`
diff --git a/src/app/debugger/remix-debugger/assets/css/font-awesome.min.css b/src/app/debugger/remix-debugger/assets/css/font-awesome.min.css
new file mode 100644
index 0000000000..540440ce89
--- /dev/null
+++ b/src/app/debugger/remix-debugger/assets/css/font-awesome.min.css
@@ -0,0 +1,4 @@
+/*!
+ * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome
+ * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
+ */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.7.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}.fa-gitlab:before{content:"\f296"}.fa-wpbeginner:before{content:"\f297"}.fa-wpforms:before{content:"\f298"}.fa-envira:before{content:"\f299"}.fa-universal-access:before{content:"\f29a"}.fa-wheelchair-alt:before{content:"\f29b"}.fa-question-circle-o:before{content:"\f29c"}.fa-blind:before{content:"\f29d"}.fa-audio-description:before{content:"\f29e"}.fa-volume-control-phone:before{content:"\f2a0"}.fa-braille:before{content:"\f2a1"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:"\f2a4"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-signing:before,.fa-sign-language:before{content:"\f2a7"}.fa-low-vision:before{content:"\f2a8"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-pied-piper:before{content:"\f2ae"}.fa-first-order:before{content:"\f2b0"}.fa-yoast:before{content:"\f2b1"}.fa-themeisle:before{content:"\f2b2"}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:"\f2b3"}.fa-fa:before,.fa-font-awesome:before{content:"\f2b4"}.fa-handshake-o:before{content:"\f2b5"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-o:before{content:"\f2b7"}.fa-linode:before{content:"\f2b8"}.fa-address-book:before{content:"\f2b9"}.fa-address-book-o:before{content:"\f2ba"}.fa-vcard:before,.fa-address-card:before{content:"\f2bb"}.fa-vcard-o:before,.fa-address-card-o:before{content:"\f2bc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-circle-o:before{content:"\f2be"}.fa-user-o:before{content:"\f2c0"}.fa-id-badge:before{content:"\f2c1"}.fa-drivers-license:before,.fa-id-card:before{content:"\f2c2"}.fa-drivers-license-o:before,.fa-id-card-o:before{content:"\f2c3"}.fa-quora:before{content:"\f2c4"}.fa-free-code-camp:before{content:"\f2c5"}.fa-telegram:before{content:"\f2c6"}.fa-thermometer-4:before,.fa-thermometer:before,.fa-thermometer-full:before{content:"\f2c7"}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-thermometer-2:before,.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:"\f2cb"}.fa-shower:before{content:"\f2cc"}.fa-bathtub:before,.fa-s15:before,.fa-bath:before{content:"\f2cd"}.fa-podcast:before{content:"\f2ce"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-times-rectangle:before,.fa-window-close:before{content:"\f2d3"}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:"\f2d4"}.fa-bandcamp:before{content:"\f2d5"}.fa-grav:before{content:"\f2d6"}.fa-etsy:before{content:"\f2d7"}.fa-imdb:before{content:"\f2d8"}.fa-ravelry:before{content:"\f2d9"}.fa-eercast:before{content:"\f2da"}.fa-microchip:before{content:"\f2db"}.fa-snowflake-o:before{content:"\f2dc"}.fa-superpowers:before{content:"\f2dd"}.fa-wpexplorer:before{content:"\f2de"}.fa-meetup:before{content:"\f2e0"}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}
diff --git a/src/app/debugger/remix-debugger/assets/fonts/FontAwesome.otf b/src/app/debugger/remix-debugger/assets/fonts/FontAwesome.otf
new file mode 100644
index 0000000000..681bdd4d4c
Binary files /dev/null and b/src/app/debugger/remix-debugger/assets/fonts/FontAwesome.otf differ
diff --git a/src/app/debugger/remix-debugger/assets/fonts/fontawesome-webfont.eot b/src/app/debugger/remix-debugger/assets/fonts/fontawesome-webfont.eot
new file mode 100644
index 0000000000..a30335d748
Binary files /dev/null and b/src/app/debugger/remix-debugger/assets/fonts/fontawesome-webfont.eot differ
diff --git a/src/app/debugger/remix-debugger/assets/fonts/fontawesome-webfont.svg b/src/app/debugger/remix-debugger/assets/fonts/fontawesome-webfont.svg
new file mode 100644
index 0000000000..6fd19abcb9
--- /dev/null
+++ b/src/app/debugger/remix-debugger/assets/fonts/fontawesome-webfont.svg
@@ -0,0 +1,640 @@
+
+
+
\ No newline at end of file
diff --git a/src/app/debugger/remix-debugger/assets/fonts/fontawesome-webfont.ttf b/src/app/debugger/remix-debugger/assets/fonts/fontawesome-webfont.ttf
new file mode 100644
index 0000000000..d7994e1308
Binary files /dev/null and b/src/app/debugger/remix-debugger/assets/fonts/fontawesome-webfont.ttf differ
diff --git a/src/app/debugger/remix-debugger/assets/fonts/fontawesome-webfont.woff b/src/app/debugger/remix-debugger/assets/fonts/fontawesome-webfont.woff
new file mode 100644
index 0000000000..6fd4ede0f3
Binary files /dev/null and b/src/app/debugger/remix-debugger/assets/fonts/fontawesome-webfont.woff differ
diff --git a/src/app/debugger/remix-debugger/assets/fonts/fontawesome-webfont.woff2 b/src/app/debugger/remix-debugger/assets/fonts/fontawesome-webfont.woff2
new file mode 100644
index 0000000000..5560193ccc
Binary files /dev/null and b/src/app/debugger/remix-debugger/assets/fonts/fontawesome-webfont.woff2 differ
diff --git a/src/app/debugger/remix-debugger/ci/browser_tests.sh b/src/app/debugger/remix-debugger/ci/browser_tests.sh
new file mode 100755
index 0000000000..b395c03481
--- /dev/null
+++ b/src/app/debugger/remix-debugger/ci/browser_tests.sh
@@ -0,0 +1,40 @@
+#!/usr/bin/env bash
+
+set -e
+
+if test $(uname -s) = "Darwin"
+then
+ OS="osx"
+ FILEFORMAT="zip"
+else
+ OS="linux"
+ FILEFORMAT="tar.gz"
+fi
+SC_VERSION="4.4.11"
+SAUCECONNECT_URL="https://saucelabs.com/downloads/sc-$SC_VERSION-$OS.$FILEFORMAT"
+SAUCECONNECT_USERNAME="yanneth"
+SAUCECONNECT_ACCESSKEY="1f5a4560-b02b-41aa-b52b-f033aad30870"
+BUILD_ID=${CIRCLE_BUILD_NUM:-${TRAVIS_JOB_NUMBER}}
+SAUCECONNECT_JOBIDENTIFIER="remix_tests_${BUILD_ID}"
+SAUCECONNECT_READYFILE="sc.ready"
+TEST_EXITCODE=0
+
+npm run build
+npm run serve &
+
+wget $SAUCECONNECT_URL
+tar -zxvf sc-"$SC_VERSION"-"$OS"."$FILEFORMAT"
+./sc-"$SC_VERSION"-$OS/bin/sc -u $SAUCECONNECT_USERNAME -k $SAUCECONNECT_ACCESSKEY -i $SAUCECONNECT_JOBIDENTIFIER --readyfile $SAUCECONNECT_READYFILE &
+while [ ! -f $SAUCECONNECT_READYFILE ]; do
+ sleep .5
+done
+
+npm run nightwatch_remote_parallel || TEST_EXITCODE=1
+
+node ci/sauceDisconnect.js $SAUCECONNECT_USERNAME $SAUCECONNECT_ACCESSKEY $SAUCECONNECT_JOBIDENTIFIER
+
+echo $TEST_EXITCODE
+if [ $TEST_EXITCODE -eq 1 ]
+then
+ exit 1
+fi
diff --git a/src/app/debugger/remix-debugger/ci/deploy_from_travis.sh b/src/app/debugger/remix-debugger/ci/deploy_from_travis.sh
new file mode 100755
index 0000000000..e80aba8e47
--- /dev/null
+++ b/src/app/debugger/remix-debugger/ci/deploy_from_travis.sh
@@ -0,0 +1,35 @@
+#!/bin/bash
+
+set -e
+
+SHA=`git rev-parse --verify HEAD`
+
+git config user.name "Travis CI"
+git config user.email "builds@ethereum.org"
+git checkout --orphan gh-pages
+git rm --cached -r .
+echo "# Automatic build" > README.md
+echo "Built website from {$SHA}. See https://github.com/ethereum/remix/ for details." >> README.md
+# -f is needed because "build" is part of .gitignore
+
+# copying file to the root folder
+cp remix-debugger/index.html index.html
+mkdir build
+cp remix-debugger/build/app.js build/app.js
+mkdir assets
+cp -R remix-debugger/assets/. assets/
+
+git add -f README.md index.html build/app.js assets
+git commit -m "Built website from {$SHA}."
+
+ENCRYPTION_LABEL=fade88419824
+ENCRYPTED_KEY_VAR="encrypted_${ENCRYPTION_LABEL}_key"
+ENCRYPTED_IV_VAR="encrypted_${ENCRYPTION_LABEL}_iv"
+ENCRYPTED_KEY=${!ENCRYPTED_KEY_VAR}
+ENCRYPTED_IV=${!ENCRYPTED_IV_VAR}
+openssl aes-256-cbc -K $ENCRYPTED_KEY -iv $ENCRYPTED_IV -in ci/deploy_key.enc -out deploy_key -d
+chmod 600 deploy_key
+eval `ssh-agent -s`
+ssh-add deploy_key
+
+git push -f git@github.com:ethereum/remix.git gh-pages
diff --git a/src/app/debugger/remix-debugger/ci/deploy_key.enc b/src/app/debugger/remix-debugger/ci/deploy_key.enc
new file mode 100644
index 0000000000..99ddf96eee
Binary files /dev/null and b/src/app/debugger/remix-debugger/ci/deploy_key.enc differ
diff --git a/src/app/debugger/remix-debugger/ci/sauceDisconnect.js b/src/app/debugger/remix-debugger/ci/sauceDisconnect.js
new file mode 100644
index 0000000000..b29328db5c
--- /dev/null
+++ b/src/app/debugger/remix-debugger/ci/sauceDisconnect.js
@@ -0,0 +1,72 @@
+const https = require('https')
+
+var userName = process.argv[2]
+var accessKey = process.argv[3]
+var tunnelName = process.argv[4]
+
+function removeTunnel () {
+ const requestPath = `/rest/v1/${userName}/tunnels`
+ console.log(requestPath)
+ callSauce(requestPath, 'GET', function (error, result) {
+ if (error) {
+ console.log(error)
+ } else {
+ var data = JSON.parse(result)
+ for (var k in data) {
+ retrieveTunnel(data[k], function (error, result) {
+ if (error) {
+ console.log(error)
+ } else if (result.identtifier === tunnelName) {
+ deleteTunnel(result.id, function () {
+ console.log('tunnel deleted ' + data[k] + ' ' + tunnelName)
+ })
+ }
+ })
+ }
+ }
+ })
+}
+
+function retrieveTunnel (tunnelid, callback) {
+ const requestPath = `/rest/v1/${userName}/tunnels/${tunnelid}`
+ callSauce(requestPath, 'GET', function (error, result) {
+ if (error) {
+ callback(error)
+ } else {
+ callback(null, {'identtifier': JSON.parse(result).tunnel_identifier, 'id': tunnelid})
+ }
+ })
+}
+
+function deleteTunnel (tunnelid, callback) {
+ const requestPath = `/rest/v1/${userName}/tunnels/${tunnelid}`
+ callSauce(requestPath, 'DELETE', callback)
+}
+
+function callSauce (requestPath, type, callback) {
+ function responseCallback (res) {
+ res.setEncoding('utf8')
+ console.log('Response: ', res.statusCode, JSON.stringify(res.headers))
+ res.on('data', function onData (chunk) {
+ console.log('BODY: ' + chunk)
+ callback(null, chunk)
+ })
+ res.on('end', function onEnd () {})
+ }
+
+ var req = https.request({
+ hostname: 'saucelabs.com',
+ path: requestPath,
+ method: type,
+ auth: userName + ':' + accessKey
+ }, responseCallback)
+
+ req.on('error', function onError (e) {
+ console.log('problem with request: ' + e.message)
+ callback(e.message)
+ })
+ req.write('')
+ req.end()
+}
+
+removeTunnel()
diff --git a/src/app/debugger/remix-debugger/findClient.js b/src/app/debugger/remix-debugger/findClient.js
new file mode 100644
index 0000000000..8ceb9bce00
--- /dev/null
+++ b/src/app/debugger/remix-debugger/findClient.js
@@ -0,0 +1,43 @@
+var which = require('which')
+
+var geth = null
+var eth = null
+
+try {
+ geth = which.sync('geth')
+} catch (e) {
+}
+
+try {
+ eth = which.sync('eth')
+} catch (e) {
+}
+if (process.argv.length > 2) {
+ if (geth && process.argv[2] === 'geth') {
+ runGeth()
+ } else if (eth && process.argv[2] === 'eth') {
+ runEth()
+ }
+} else if (geth && eth) {
+ console.log('both eth and geth has been found in your system')
+ console.log('restart the command with the desired client:')
+ console.log('npm run start_eth')
+ console.log('or')
+ console.log('npm run start_geth')
+} else if (geth) {
+ runGeth()
+} else if (eth) {
+ runEth()
+} else {
+ console.log('neither eth or geth has been found in your system')
+}
+
+function runEth () {
+ console.log('starting eth...')
+ process.exit(20)
+}
+
+function runGeth () {
+ console.log('starting geth...')
+ process.exit(21)
+}
diff --git a/src/app/debugger/remix-debugger/index.html b/src/app/debugger/remix-debugger/index.html
new file mode 100644
index 0000000000..4c6c33cf4c
--- /dev/null
+++ b/src/app/debugger/remix-debugger/index.html
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/app/debugger/remix-debugger/index.js b/src/app/debugger/remix-debugger/index.js
new file mode 100644
index 0000000000..a113f9390d
--- /dev/null
+++ b/src/app/debugger/remix-debugger/index.js
@@ -0,0 +1,24 @@
+'use strict'
+var VMDebugger = require('./src/ui/VmDebugger')
+var Debugger = require('./src/ui/Ethdebugger')
+var BasicPanel = require('./src/ui/BasicPanel')
+var TreeView = require('./src/ui/TreeView')
+
+if (typeof (module) !== 'undefined' && typeof (module.exports) !== 'undefined') {
+ module.exports = modules()
+}
+
+if (typeof (window) !== 'undefined') {
+ window.remix = modules()
+}
+
+function modules () {
+ return {
+ ui: {
+ Debugger: Debugger,
+ VMdebugger: VMDebugger,
+ BasicPanel: BasicPanel,
+ TreeView: TreeView
+ }
+ }
+}
diff --git a/src/app/debugger/remix-debugger/nightwatch.js b/src/app/debugger/remix-debugger/nightwatch.js
new file mode 100644
index 0000000000..b81c8176b5
--- /dev/null
+++ b/src/app/debugger/remix-debugger/nightwatch.js
@@ -0,0 +1,101 @@
+'use strict'
+var buildId = process.env.CIRCLE_BUILD_NUM || process.env.TRAVIS_JOB_NUMBER
+
+module.exports = {
+ 'src_folders': ['./test-browser/test'],
+ 'output_folder': './test-browser/test/reports',
+ 'custom_commands_path': '',
+ 'custom_assertions_path': '',
+ 'globals_path': '',
+ 'page_objects_path': '',
+
+ 'selenium': {
+ 'start_process': false,
+ 'server_path': '',
+ 'log_path': '',
+ 'host': '127.0.0.1',
+ 'port': 4444,
+ 'cli_args': {
+ 'webdriver.chrome.driver': '',
+ 'webdriver.ie.driver': '',
+ 'webdriver.firefox.profile': ''
+ }
+ },
+
+ 'test_settings': {
+ 'default': {
+ 'launch_url': 'http://ondemand.saucelabs.com:80',
+ 'selenium_host': 'ondemand.saucelabs.com',
+ 'selenium_port': 80,
+ 'silent': true,
+ 'username': 'yanneth',
+ 'access_key': '1f5a4560-b02b-41aa-b52b-f033aad30870',
+ 'use_ssl': false,
+ 'globals': {
+ 'waitForConditionTimeout': 10000,
+ 'asyncHookTimeout': 100000
+ },
+ 'screenshots': {
+ 'enabled': false,
+ 'path': ''
+ },
+ 'desiredCapabilities': {
+ 'browserName': 'firefox',
+ 'javascriptEnabled': true,
+ 'acceptSslCerts': true,
+ 'build': 'build-' + buildId,
+ 'tunnel-identifier': 'remix_tests_' + buildId
+ }
+ },
+
+ 'chrome': {
+ 'desiredCapabilities': {
+ 'browserName': 'chrome',
+ 'javascriptEnabled': true,
+ 'acceptSslCerts': true,
+ 'build': 'build-' + buildId,
+ 'tunnel-identifier': 'remix_tests_' + buildId
+ }
+ },
+
+ 'safari': {
+ 'desiredCapabilities': {
+ 'browserName': 'safari',
+ 'javascriptEnabled': true,
+ 'platform': 'OS X 10.11',
+ 'version': '10.0',
+ 'acceptSslCerts': true,
+ 'build': 'build-' + buildId,
+ 'tunnel-identifier': 'remix_tests_' + buildId
+ }
+ },
+
+ 'ie': {
+ 'desiredCapabilities': {
+ 'browserName': 'internet explorer',
+ 'javascriptEnabled': true,
+ 'acceptSslCerts': true,
+ 'platform': 'WIN8.1',
+ 'version': '11',
+ 'build': 'build-' + buildId,
+ 'tunnel-identifier': 'remix_tests_' + buildId
+ }
+ },
+
+ 'local': {
+ 'launch_url': 'http://localhost',
+ 'selenium_host': '127.0.0.1',
+ 'selenium_port': 4444,
+ 'silent': true,
+ 'screenshots': {
+ 'enabled': false,
+ 'path': ''
+ },
+ 'desiredCapabilities': {
+ 'browserName': 'firefox',
+ 'javascriptEnabled': true,
+ 'acceptSslCerts': true
+ }
+ }
+ }
+}
diff --git a/src/app/debugger/remix-debugger/package.json b/src/app/debugger/remix-debugger/package.json
new file mode 100644
index 0000000000..00fbfc8bcb
--- /dev/null
+++ b/src/app/debugger/remix-debugger/package.json
@@ -0,0 +1,150 @@
+{
+ "name": "remix-debugger",
+ "version": "0.1.2",
+ "description": "Ethereum IDE and tools for the web",
+ "contributors": [
+ {
+ "name": "Yann Levreau",
+ "email": "yann@ethdev.com"
+ },
+ {
+ "name": "Liana Husikyan",
+ "email": "liana@ethdev.com"
+ }
+ ],
+ "main": "./index.js",
+ "devDependencies": {
+ "babel-eslint": "^7.1.1",
+ "babel-plugin-transform-object-assign": "^6.22.0",
+ "babel-plugin-yo-yoify": "^0.3.3",
+ "babel-polyfill": "^6.22.0",
+ "babel-preset-env": "^1.6.1",
+ "babel-preset-es2015": "^6.24.0",
+ "babel-preset-stage-0": "^6.24.1",
+ "babelify": "^7.3.0",
+ "browserify": "^13.0.1",
+ "browserify-livereload": "^1.0.10",
+ "clipboard-copy": "^1.2.0",
+ "csjs-inject": "^1.0.1",
+ "ethereum-common": "0.0.18",
+ "ethereumjs-block": "^1.2.2",
+ "ethereumjs-tx": "^1.1.1",
+ "ethereumjs-util": "^4.5.0",
+ "ethereumjs-vm": "2.3.1",
+ "fast-async": "^6.1.2",
+ "http-server": "^0.9.0",
+ "nightwatch": "^0.9.5",
+ "notify-error": "^1.2.0",
+ "npm-run-all": "^4.1.2",
+ "onchange": "^3.3.0",
+ "remix-core": "latest",
+ "remix-lib": "latest",
+ "remix-solidity": "latest",
+ "selenium-standalone": "^6.0.1",
+ "solc": "^0.4.13",
+ "standard": "^7.0.1",
+ "standard-reporter": "^1.0.5",
+ "tape": "^4.6.0",
+ "watchify": "^3.9.0",
+ "web3": "^0.15.3",
+ "yo-yo": "^1.2.1",
+ "yo-yoify": "^3.1.0"
+ },
+ "scripts": {
+ "build": "mkdirp build; browserify index.js > build/app.js",
+ "lint": "standard | notify-error",
+ "nightwatch_local": "nightwatch --config nightwatch.js --env local",
+ "nightwatch_remote_chrome": "nightwatch --config nightwatch.js --env chrome",
+ "nightwatch_remote_firefox": "nightwatch --config nightwatch.js --env default",
+ "nightwatch_remote_ie": "nightwatch --config nightwatch.js --env ie",
+ "nightwatch_remote_parallel": "nightwatch --config nightwatch.js --env safari,chrome,default",
+ "nightwatch_remote_safari": "nightwatch --config nightwatch.js --env safari",
+ "onchange": "onchange build/app.js -- npm run lint",
+ "selenium": "selenium-standalone start",
+ "selenium-install": "selenium-standalone install",
+ "serve": "http-server .",
+ "start": "./runNode.sh",
+ "start_dev": "npm-run-all -lpr serve watch onchange",
+ "start_eth": "npm run warning_message; eth -j --rpccorsdomain '*'",
+ "start_geth": "npm run warning_message; geth --rpc --rpcapi 'web3,eth,debug' --rpcport 8545 --rpccorsdomain '*'",
+ "test": "standard && tape ./test/tests.js && ./ci/browser_tests.sh",
+ "test-browser": "npm-run-all -lpr selenium serve waittest",
+ "waittest": "sleep 5 && npm run nightwatch_local",
+ "warning_message": "echo 'DO NOT DO THIS IF eth/geth STORES PRIVATE KEYS!! External system might be able to access your node through the RPC server.\n\n';",
+ "watch": "mkdirp build; watchify index.js -p [ browserify-livereload --host 127.0.0.1 --port 1337 ] -dv -o build/app.js"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/ethereum/remix.git"
+ },
+ "author": "cpp-ethereum team",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/ethereum/remix/issues"
+ },
+ "homepage": "https://github.com/ethereum/remix#readme",
+ "standard": {
+ "ignore": [
+ "node_modules/*",
+ "build/*",
+ "test/resources/*"
+ ]
+ },
+ "babel": {
+ "plugins": [
+ "transform-es2015-template-literals",
+ "transform-es2015-literals",
+ "transform-es2015-function-name",
+ "transform-es2015-arrow-functions",
+ "transform-es2015-block-scoped-functions",
+ "transform-es2015-classes",
+ "transform-es2015-object-super",
+ "transform-es2015-shorthand-properties",
+ "transform-es2015-duplicate-keys",
+ "transform-es2015-computed-properties",
+ "transform-es2015-for-of",
+ "transform-es2015-sticky-regex",
+ "transform-es2015-unicode-regex",
+ "check-es2015-constants",
+ "transform-es2015-spread",
+ "transform-es2015-parameters",
+ "transform-es2015-destructuring",
+ "transform-es2015-block-scoping",
+ "transform-object-assign"
+ ]
+ },
+ "browserify": {
+ "transform": [
+ [
+ "babelify",
+ {
+ "sourceMapsAbsolute": false,
+ "sourceMaps": true,
+ "plugins": [
+ [
+ "fast-async",
+ {
+ "runtimePattern": null,
+ "compiler": {
+ "es7": true,
+ "noRuntime": true,
+ "promises": true,
+ "wrapAwait": true
+ }
+ }
+ ],
+ [
+ "yo-yoify"
+ ],
+ [
+ "transform-object-assign"
+ ]
+ ],
+ "presets": [
+ "es2015"
+ ]
+ }
+ ]
+ ]
+ }
+}
diff --git a/src/app/debugger/remix-debugger/runNode.sh b/src/app/debugger/remix-debugger/runNode.sh
new file mode 100755
index 0000000000..79575a46f2
--- /dev/null
+++ b/src/app/debugger/remix-debugger/runNode.sh
@@ -0,0 +1,14 @@
+#!/usr/bin/env bash
+
+node findClient.js $1
+RUNCLIENT=$?
+if [ $RUNCLIENT -eq '20' ]
+then
+ npm run start_eth
+fi
+
+if [ $RUNCLIENT -eq '21' ]
+then
+ echo $?
+ npm run start_geth
+fi
\ No newline at end of file
diff --git a/src/app/debugger/remix-debugger/src/ui/BasicPanel.js b/src/app/debugger/remix-debugger/src/ui/BasicPanel.js
new file mode 100644
index 0000000000..6efed6231c
--- /dev/null
+++ b/src/app/debugger/remix-debugger/src/ui/BasicPanel.js
@@ -0,0 +1,51 @@
+'use strict'
+var style = require('./styles/basicStyles')
+var yo = require('yo-yo')
+var remixLib = require('remix-lib')
+var ui = remixLib.helpers.ui
+
+var csjs = require('csjs-inject')
+
+var css = csjs`
+ .container {
+ width: 70%;
+ }
+`
+
+function BasicPanel (_name, _width, _height) {
+ this.data
+ this.name = _name
+ this.width = _width
+ this.height = _height
+ this.view
+}
+
+BasicPanel.prototype.update = function () {
+ yo.update(this.view, this.render())
+}
+
+BasicPanel.prototype.hide = function () {
+ this.view.style.display = 'none'
+}
+
+BasicPanel.prototype.show = function () {
+ this.view.style.display = 'block'
+}
+
+BasicPanel.prototype.render = function () {
+ var view = yo`
+ `
+ if (!this.view) {
+ this.view = view
+ }
+ return view
+}
+
+module.exports = BasicPanel
diff --git a/src/app/debugger/remix-debugger/src/ui/ButtonNavigator.js b/src/app/debugger/remix-debugger/src/ui/ButtonNavigator.js
new file mode 100644
index 0000000000..f438d69236
--- /dev/null
+++ b/src/app/debugger/remix-debugger/src/ui/ButtonNavigator.js
@@ -0,0 +1,183 @@
+'use strict'
+var remixLib = require('remix-lib')
+var EventManager = remixLib.EventManager
+var yo = require('yo-yo')
+
+var csjs = require('csjs-inject')
+var styleGuide = remixLib.ui.themeChooser
+var styles = styleGuide.chooser()
+
+var css = csjs`
+ .buttons {
+ display: flex;
+ flex-wrap: wrap;
+ }
+ .stepButtons {
+ width: 100%;
+ display: flex;
+ justify-content: center;
+ }
+ .stepButton {
+ ${styles.rightPanel.debuggerTab.button_Debugger}
+ }
+ .jumpButtons {
+ width: 100%;
+ display: flex;
+ justify-content: center;
+ }
+ .jumpButton {
+ ${styles.rightPanel.debuggerTab.button_Debugger}
+ }
+ .navigator {
+ color: ${styles.rightPanel.debuggerTab.text_Primary};
+ }
+ .navigator:hover {
+ color: ${styles.rightPanel.debuggerTab.button_Debugger_icon_HoverColor};
+ }
+`
+
+function ButtonNavigator (_parent, _traceManager) {
+ this.event = new EventManager()
+ this.intoBackDisabled = true
+ this.overBackDisabled = true
+ this.intoForwardDisabled = true
+ this.overForwardDisabled = true
+ this.jumpOutDisabled = true
+ this.jumpNextBreakpointDisabled = true
+ this.jumpPreviousBreakpointDisabled = true
+
+ this.traceManager = _traceManager
+ this.currentCall = null
+ this.revertionPoint = null
+
+ _parent.event.register('indexChanged', this, (index) => {
+ if (index < 0) return
+ if (_parent.currentStepIndex !== index) return
+
+ this.traceManager.buildCallPath(index, (error, callsPath) => {
+ if (error) {
+ console.log(error)
+ resetWarning(this)
+ } else {
+ this.currentCall = callsPath[callsPath.length - 1]
+ if (this.currentCall.reverted) {
+ this.revertionPoint = this.currentCall.return
+ this.view.querySelector('#reverted').style.display = 'block'
+ this.view.querySelector('#reverted #outofgas').style.display = this.currentCall.outOfGas ? 'inline' : 'none'
+ this.view.querySelector('#reverted #parenthasthrown').style.display = 'none'
+ } else {
+ var k = callsPath.length - 2
+ while (k >= 0) {
+ var parent = callsPath[k]
+ if (parent.reverted) {
+ this.revertionPoint = parent.return
+ this.view.querySelector('#reverted').style.display = 'block'
+ this.view.querySelector('#reverted #parenthasthrown').style.display = parent ? 'inline' : 'none'
+ this.view.querySelector('#reverted #outofgas').style.display = 'none'
+ return
+ }
+ k--
+ }
+ resetWarning(this)
+ }
+ }
+ })
+ })
+
+ this.view
+}
+
+module.exports = ButtonNavigator
+
+ButtonNavigator.prototype.render = function () {
+ var self = this
+ var view = yo``
+ if (!this.view) {
+ this.view = view
+ }
+ return view
+}
+
+ButtonNavigator.prototype.reset = function () {
+ this.intoBackDisabled = true
+ this.overBackDisabled = true
+ this.intoForwardDisabled = true
+ this.overForwardDisabled = true
+ this.jumpOutDisabled = true
+ this.jumpNextBreakpointDisabled = true
+ this.jumpPreviousBreakpointDisabled = true
+ resetWarning(this)
+}
+
+ButtonNavigator.prototype.stepChanged = function (step) {
+ this.intoBackDisabled = step <= 0
+ this.overBackDisabled = step <= 0
+ if (!this.traceManager) {
+ this.intoForwardDisabled = true
+ this.overForwardDisabled = true
+ } else {
+ var self = this
+ this.traceManager.getLength(function (error, length) {
+ if (error) {
+ self.reset()
+ console.log(error)
+ } else {
+ self.jumpNextBreakpointDisabled = step >= length - 1
+ self.jumpPreviousBreakpointDisabled = step <= 0
+ self.intoForwardDisabled = step >= length - 1
+ self.overForwardDisabled = step >= length - 1
+ var stepOut = self.traceManager.findStepOut(step)
+ self.jumpOutDisabled = stepOut === step
+ }
+ self.updateAll()
+ })
+ }
+ this.updateAll()
+}
+
+ButtonNavigator.prototype.updateAll = function () {
+ this.updateDisabled('intoback', this.intoBackDisabled)
+ this.updateDisabled('overback', this.overBackDisabled)
+ this.updateDisabled('overforward', this.overForwardDisabled)
+ this.updateDisabled('intoforward', this.intoForwardDisabled)
+ this.updateDisabled('jumpout', this.jumpOutDisabled)
+ this.updateDisabled('jumptoexception', this.jumpOutDisabled)
+ this.updateDisabled('jumpnextbreakpoint', this.jumpNextBreakpointDisabled)
+ this.updateDisabled('jumppreviousbreakpoint', this.jumpPreviousBreakpointDisabled)
+}
+
+ButtonNavigator.prototype.updateDisabled = function (id, disabled) {
+ if (disabled) {
+ document.getElementById(id).setAttribute('disabled', true)
+ } else {
+ document.getElementById(id).removeAttribute('disabled')
+ }
+}
+
+function resetWarning (self) {
+ self.view.querySelector('#reverted #outofgas').style.display = 'none'
+ self.view.querySelector('#reverted #parenthasthrown').style.display = 'none'
+ self.view.querySelector('#reverted').style.display = 'none'
+}
+
+module.exports = ButtonNavigator
diff --git a/src/app/debugger/remix-debugger/src/ui/CalldataPanel.js b/src/app/debugger/remix-debugger/src/ui/CalldataPanel.js
new file mode 100644
index 0000000000..e2fef57b74
--- /dev/null
+++ b/src/app/debugger/remix-debugger/src/ui/CalldataPanel.js
@@ -0,0 +1,33 @@
+'use strict'
+var DropdownPanel = require('./DropdownPanel')
+var yo = require('yo-yo')
+
+function CalldataPanel (_parent, _traceManager) {
+ this.parent = _parent
+ this.traceManager = _traceManager
+ this.basicPanel = new DropdownPanel('Call Data', {json: true})
+ this.init()
+}
+
+CalldataPanel.prototype.render = function () {
+ return yo`${this.basicPanel.render()}
`
+}
+
+CalldataPanel.prototype.init = function () {
+ var self = this
+ this.parent.event.register('indexChanged', this, function (index) {
+ if (index < 0) return
+ if (self.parent.currentStepIndex !== index) return
+
+ self.traceManager.getCallDataAt(index, function (error, calldata) {
+ if (error) {
+ self.basicPanel.update({})
+ console.log(error)
+ } else if (self.parent.currentStepIndex === index) {
+ self.basicPanel.update(calldata)
+ }
+ })
+ })
+}
+
+module.exports = CalldataPanel
diff --git a/src/app/debugger/remix-debugger/src/ui/CallstackPanel.js b/src/app/debugger/remix-debugger/src/ui/CallstackPanel.js
new file mode 100644
index 0000000000..a9ecdbac5e
--- /dev/null
+++ b/src/app/debugger/remix-debugger/src/ui/CallstackPanel.js
@@ -0,0 +1,33 @@
+'use strict'
+var DropdownPanel = require('./DropdownPanel')
+var yo = require('yo-yo')
+
+function CallstackPanel (_parent, _traceManager) {
+ this.parent = _parent
+ this.traceManager = _traceManager
+ this.basicPanel = new DropdownPanel('Call Stack', {json: true})
+ this.init()
+}
+
+CallstackPanel.prototype.render = function () {
+ return yo`${this.basicPanel.render()}
`
+}
+
+CallstackPanel.prototype.init = function () {
+ var self = this
+ this.parent.event.register('indexChanged', this, function (index) {
+ if (index < 0) return
+ if (self.parent.currentStepIndex !== index) return
+
+ self.traceManager.getCallStackAt(index, function (error, callstack) {
+ if (error) {
+ console.log(error)
+ self.basicPanel.update({})
+ } else if (self.parent.currentStepIndex === index) {
+ self.basicPanel.update(callstack)
+ }
+ })
+ })
+}
+
+module.exports = CallstackPanel
diff --git a/src/app/debugger/remix-debugger/src/ui/CodeListView.js b/src/app/debugger/remix-debugger/src/ui/CodeListView.js
new file mode 100644
index 0000000000..784d6cbfe4
--- /dev/null
+++ b/src/app/debugger/remix-debugger/src/ui/CodeListView.js
@@ -0,0 +1,89 @@
+'use strict'
+var style = require('./styles/basicStyles')
+var yo = require('yo-yo')
+var remixLib = require('remix-lib')
+var DropdownPanel = require('./DropdownPanel')
+var EventManager = remixLib.EventManager
+var csjs = require('csjs-inject')
+var styleGuide = remixLib.ui.themeChooser
+var styles = styleGuide.chooser()
+
+var css = csjs`
+ .instructions {
+ ${styles.rightPanel.debuggerTab.box_Debugger}
+ width: 75%;
+ overflow-y: scroll;
+ max-height: 250px;
+ }
+`
+function CodeListView (_parent, _codeManager) {
+ this.event = new EventManager()
+ this.parent = _parent
+ this.codeManager = _codeManager
+ this.code
+ this.address
+ this.codeView
+ this.itemSelected
+ this.basicPanel = new DropdownPanel('Instructions', {json: false})
+ this.basicPanel.event.register('hide', () => {
+ this.event.trigger('hide', [])
+ })
+ this.basicPanel.event.register('show', () => {
+ this.event.trigger('show', [])
+ })
+ this.init()
+}
+
+CodeListView.prototype.render = function () {
+ return yo`${this.basicPanel.render({height: style.instructionsList.height})}
`
+}
+
+CodeListView.prototype.init = function () {
+ var self = this
+ this.codeManager.event.register('changed', this, this.changed)
+ this.parent.event.register('traceUnloaded', this, function () {
+ self.changed([], '', -1)
+ })
+}
+
+CodeListView.prototype.indexChanged = function (index) {
+ if (index >= 0) {
+ if (this.itemSelected) {
+ this.itemSelected.removeAttribute('selected')
+ this.itemSelected.removeAttribute('style')
+ if (this.itemSelected.firstChild) {
+ this.itemSelected.firstChild.removeAttribute('style')
+ }
+ }
+ this.itemSelected = this.codeView.children[index]
+ this.itemSelected.setAttribute('style', 'background-color: ' + styles.rightPanel.debuggerTab.text_BgHighlight)
+ this.itemSelected.setAttribute('selected', 'selected')
+ if (this.itemSelected.firstChild) {
+ this.itemSelected.firstChild.setAttribute('style', 'margin-left: 2px')
+ }
+ this.codeView.scrollTop = this.itemSelected.offsetTop - parseInt(this.codeView.offsetTop)
+ }
+}
+
+CodeListView.prototype.changed = function (code, address, index) {
+ if (this.address !== address) {
+ this.code = code
+ this.address = address
+ this.codeView = this.renderAssemblyItems()
+ this.basicPanel.setContent(this.codeView)
+ }
+ this.indexChanged(index)
+}
+
+CodeListView.prototype.renderAssemblyItems = function () {
+ if (this.code) {
+ var codeView = this.code.map(function (item, i) {
+ return yo`${item}
`
+ })
+ return yo`
+ ${codeView}
+
`
+ }
+}
+
+module.exports = CodeListView
diff --git a/src/app/debugger/remix-debugger/src/ui/DropdownPanel.js b/src/app/debugger/remix-debugger/src/ui/DropdownPanel.js
new file mode 100644
index 0000000000..17337d49e9
--- /dev/null
+++ b/src/app/debugger/remix-debugger/src/ui/DropdownPanel.js
@@ -0,0 +1,201 @@
+'use strict'
+var yo = require('yo-yo')
+const copy = require('clipboard-copy')
+var remixLib = require('remix-lib')
+var TreeView = require('./TreeView')
+var EventManager = remixLib.EventManager
+
+var csjs = require('csjs-inject')
+var styleGuide = remixLib.ui.themeChooser
+var styles = styleGuide.chooser()
+
+var css = csjs`
+ .title {
+ margin-top: 10px;
+ ${styles.rightPanel.debuggerTab.dropdown_Debugger};
+ display: flex;
+ align-items: center;
+ }
+ .name {
+ font-weight: bold;
+ }
+ .nameDetail {
+ font-weight: bold;
+ margin-left: 3px;
+ }
+ .icon {
+ color: ${styles.rightPanel.debuggerTab.button_Debugger_icon_Color};
+ margin-right: 5%;
+ }
+ .eyeButton {
+ margin: 3px;
+ }
+ .eyeButton:hover {
+ color: ${styles.rightPanel.debuggerTab.button_Debugger_icon_HoverColor};
+ }
+ .dropdownpanel {
+ ${styles.rightPanel.debuggerTab.dropdown_Debugger};
+ width: 100%;
+ }
+ .dropdownrawcontent {
+ padding: 2px;
+ word-break: break-all;
+ }
+ .message {
+ padding: 2px;
+ word-break: break-all;
+ }
+ .refresh {
+ display: none;
+ margin-left: 4px;
+ margin-top: 4px;
+ animation: spin 2s linear infinite;
+ }
+`
+
+function DropdownPanel (_name, _opts) {
+ this.event = new EventManager()
+ if (!_opts) {
+ _opts = {}
+ }
+ this.name = _name
+ this.header = ''
+ this.json = _opts.json
+ if (this.json) {
+ this.treeView = new TreeView(_opts)
+ }
+ this.view
+}
+
+DropdownPanel.prototype.setMessage = function (message) {
+ if (this.view) {
+ this.view.querySelector('.dropdownpanel .dropdownrawcontent').style.display = 'none'
+ this.view.querySelector('.dropdownpanel .dropdowncontent').style.display = 'none'
+ this.view.querySelector('.dropdownpanel .fa-refresh').style.display = 'none'
+ this.message(message)
+ }
+}
+
+DropdownPanel.prototype.setLoading = function () {
+ if (this.view) {
+ this.view.querySelector('.dropdownpanel .dropdownrawcontent').style.display = 'none'
+ this.view.querySelector('.dropdownpanel .dropdowncontent').style.display = 'none'
+ this.view.querySelector('.dropdownpanel .fa-refresh').style.display = 'inline-block'
+ this.message('')
+ }
+}
+
+DropdownPanel.prototype.setUpdating = function () {
+ if (this.view) {
+ this.view.querySelector('.dropdownpanel .dropdowncontent').style.color = styles.appProperties.greyedText_color
+ }
+}
+
+DropdownPanel.prototype.update = function (_data, _header) {
+ if (this.view) {
+ this.view.querySelector('.dropdownpanel .fa-refresh').style.display = 'none'
+ this.view.querySelector('.dropdownpanel .dropdowncontent').style.display = 'block'
+ this.view.querySelector('.dropdownpanel .dropdowncontent').style.color = styles.appProperties.mainText_Color
+ this.view.querySelector('.dropdownpanel .dropdownrawcontent').innerText = JSON.stringify(_data, null, '\t')
+ this.view.querySelector('.title div.btn').style.display = 'block'
+ this.view.querySelector('.title span').innerText = _header || ' '
+ this.message('')
+ if (this.json) {
+ this.treeView.update(_data)
+ }
+ }
+}
+
+DropdownPanel.prototype.setContent = function (node) {
+ if (this.view) {
+ var parent = this.view.querySelector('.dropdownpanel div.dropdowncontent')
+ parent.replaceChild(node, parent.firstElementChild)
+ }
+}
+
+DropdownPanel.prototype.render = function (overridestyle) {
+ var content = yo`Empty
`
+ if (this.json) {
+ content = this.treeView.render({})
+ }
+ overridestyle === undefined ? {} : overridestyle
+ var self = this
+ var view = yo`
+ `
+ if (!this.view) {
+ this.view = view
+ }
+ return view
+}
+
+DropdownPanel.prototype.copyClipboard = function () {
+ var content = this.view.querySelector('.dropdownpanel .dropdownrawcontent')
+ if (content) copy(content.innerText ? content.innerText : content.textContent)
+}
+
+DropdownPanel.prototype.toggle = function () {
+ var el = this.view.querySelector('.dropdownpanel')
+ var caret = this.view.querySelector('.title').firstElementChild
+ if (el.style.display === '') {
+ el.style.display = 'none'
+ caret.className = `${css.icon} fa fa-caret-right`
+ this.event.trigger('hide', [])
+ } else {
+ el.style.display = ''
+ caret.className = `${css.icon} fa fa-caret-down`
+ this.event.trigger('show', [])
+ }
+}
+
+DropdownPanel.prototype.hide = function () {
+ if (this.view) {
+ var caret = this.view.querySelector('.title').firstElementChild
+ var el = this.view.querySelector('.dropdownpanel')
+ el.style.display = 'none'
+ caret.className = `${css.icon} fa fa-caret-right`
+ this.event.trigger('hide', [])
+ }
+}
+
+DropdownPanel.prototype.show = function () {
+ if (this.view) {
+ var caret = this.view.querySelector('.title').firstElementChild
+ var el = this.view.querySelector('.dropdownpanel')
+ el.style.display = ''
+ caret.className = `${css.icon} fa fa-caret-down`
+ this.event.trigger('show', [])
+ }
+}
+
+DropdownPanel.prototype.message = function (message) {
+ if (this.view) {
+ var mes = this.view.querySelector('.dropdownpanel .message')
+ mes.innerText = message
+ mes.style.display = (message === '') ? 'none' : 'block'
+ }
+}
+
+module.exports = DropdownPanel
diff --git a/src/app/debugger/remix-debugger/src/ui/Ethdebugger.js b/src/app/debugger/remix-debugger/src/ui/Ethdebugger.js
new file mode 100644
index 0000000000..b799c0cc59
--- /dev/null
+++ b/src/app/debugger/remix-debugger/src/ui/Ethdebugger.js
@@ -0,0 +1,185 @@
+'use strict'
+var TxBrowser = require('./TxBrowser')
+var StepManager = require('./StepManager')
+var remixCore = require('remix-core')
+var TraceManager = remixCore.trace.TraceManager
+var VmDebugger = require('./VmDebugger')
+var remixLib = require('remix-lib')
+var global = remixLib.global
+var init = remixLib.init
+var executionContext = remixLib.execution.executionContext
+var EventManager = remixLib.EventManager
+var yo = require('yo-yo')
+var csjs = require('csjs-inject')
+var Web3Providers = remixLib.vm.Web3Providers
+var DummyProvider = remixLib.vm.DummyProvider
+var CodeManager = remixCore.code.CodeManager
+var remixSolidity = require('remix-solidity')
+var SolidityProxy = remixSolidity.SolidityProxy
+var InternalCallTree = remixSolidity.InternalCallTree
+
+var css = csjs`
+ .statusMessage {
+ margin-left: 15px;
+ }
+ .innerShift {
+ padding: 2px;
+ margin-left: 10px;
+ }
+`
+
+function Ethdebugger (opts) {
+ this.opts = opts || {}
+ if (!this.opts.compilationResult) this.opts.compilationResult = () => { return null }
+
+ var self = this
+ this.event = new EventManager()
+
+ this.currentStepIndex = -1
+ this.tx
+ this.statusMessage = ''
+
+ this.view
+ this.web3Providers = new Web3Providers()
+ this.addProvider('DUMMYWEB3', new DummyProvider())
+ this.switchProvider('DUMMYWEB3')
+ this.traceManager = new TraceManager()
+ this.codeManager = new CodeManager(this.traceManager)
+ this.solidityProxy = new SolidityProxy(this.traceManager, this.codeManager)
+
+ var callTree = new InternalCallTree(this.event, this.traceManager, this.solidityProxy, this.codeManager, { includeLocalVariables: true })
+ this.callTree = callTree // TODO: currently used by browser solidity, we should improve the API
+
+ this.event.register('indexChanged', this, function (index) {
+ self.codeManager.resolveStep(index, self.tx)
+ })
+
+ this.txBrowser = new TxBrowser(this)
+ this.txBrowser.event.register('newTxLoading', this, function () {
+ self.unLoad()
+ })
+ this.txBrowser.event.register('newTraceRequested', this, function (blockNumber, txIndex, tx) {
+ self.startDebugging(blockNumber, txIndex, tx)
+ })
+ this.txBrowser.event.register('unloadRequested', this, function (blockNumber, txIndex, tx) {
+ self.unLoad()
+ })
+ this.stepManager = new StepManager(this, this.traceManager)
+ this.stepManager.event.register('stepChanged', this, function (stepIndex) {
+ self.stepChanged(stepIndex)
+ })
+ this.vmDebugger = new VmDebugger(this, this.traceManager, this.codeManager, this.solidityProxy, callTree)
+
+ this.codeManager.event.register('changed', this, (code, address, instIndex) => {
+ this.callTree.sourceLocationTracker.getSourceLocationFromVMTraceIndex(address, this.currentStepIndex, this.solidityProxy.contracts, (error, sourceLocation) => {
+ if (!error) {
+ this.event.trigger('sourceLocationChanged', [sourceLocation])
+ }
+ })
+ })
+}
+
+Ethdebugger.prototype.setBreakpointManager = function (breakpointManager) {
+ this.breakpointManager = breakpointManager
+}
+
+Ethdebugger.prototype.web3 = function () {
+ return global.web3
+}
+
+Ethdebugger.prototype.addProvider = function (type, obj) {
+ this.web3Providers.addProvider(type, obj)
+ this.event.trigger('providerAdded', [type])
+}
+
+Ethdebugger.prototype.switchProvider = function (type) {
+ var self = this
+ this.web3Providers.get(type, function (error, obj) {
+ if (error) {
+ console.log('provider ' + type + ' not defined')
+ } else {
+ global.web3 = obj
+ executionContext.detectNetwork((error, network) => {
+ if (error || !network) {
+ global.web3Debug = obj
+ } else {
+ var webDebugNode = init.web3DebugNode(network.name)
+ global.web3Debug = !webDebugNode ? obj : webDebugNode
+ }
+ })
+ self.event.trigger('providerChanged', [type])
+ }
+ })
+}
+
+Ethdebugger.prototype.setCompilationResult = function (compilationResult) {
+ if (compilationResult && compilationResult.sources && compilationResult.contracts) {
+ this.solidityProxy.reset(compilationResult)
+ } else {
+ this.solidityProxy.reset({})
+ }
+}
+
+Ethdebugger.prototype.debug = function (tx) {
+ this.setCompilationResult(this.opts.compilationResult())
+ if (tx instanceof Object) {
+ this.txBrowser.load(tx.hash)
+ } else if (tx instanceof String) {
+ this.txBrowser.load(tx)
+ }
+}
+
+Ethdebugger.prototype.render = function () {
+ var view = yo`
+
+ ${this.txBrowser.render()}
+ ${this.stepManager.render()}
+
+
${this.statusMessage}
+ ${this.vmDebugger.render()}
+
`
+ if (!this.view) {
+ this.view = view
+ }
+ return view
+}
+
+Ethdebugger.prototype.unLoad = function () {
+ this.traceManager.init()
+ this.codeManager.clear()
+ this.stepManager.reset()
+ this.event.trigger('traceUnloaded')
+}
+
+Ethdebugger.prototype.stepChanged = function (stepIndex) {
+ this.currentStepIndex = stepIndex
+ this.event.trigger('indexChanged', [stepIndex])
+}
+
+Ethdebugger.prototype.startDebugging = function (blockNumber, txIndex, tx) {
+ if (this.traceManager.isLoading) {
+ return
+ }
+ this.setCompilationResult(this.opts.compilationResult())
+ this.statusMessage = 'Loading trace...'
+ yo.update(this.view, this.render())
+ console.log('loading trace...')
+ this.tx = tx
+ var self = this
+ this.traceManager.resolveTrace(tx, function (error, result) {
+ console.log('trace loaded ' + result)
+ if (result) {
+ self.statusMessage = ''
+ yo.update(self.view, self.render())
+ self.event.trigger('newTraceLoaded', [self.traceManager.trace])
+ if (self.breakpointManager && self.breakpointManager.hasBreakpoint()) {
+ self.breakpointManager.jumpNextBreakpoint(false)
+ }
+ } else {
+ self.statusMessage = error ? error.message : 'Trace not loaded'
+ yo.update(self.view, self.render())
+ }
+ })
+}
+
+module.exports = Ethdebugger
diff --git a/src/app/debugger/remix-debugger/src/ui/FullStoragesChanges.js b/src/app/debugger/remix-debugger/src/ui/FullStoragesChanges.js
new file mode 100644
index 0000000000..ef454851c2
--- /dev/null
+++ b/src/app/debugger/remix-debugger/src/ui/FullStoragesChanges.js
@@ -0,0 +1,71 @@
+'use strict'
+var DropdownPanel = require('./DropdownPanel')
+var remixCore = require('remix-core')
+var StorageViewer = remixCore.storage.StorageViewer
+var yo = require('yo-yo')
+
+function FullStoragesChanges (_parent, _traceManager) {
+ this.storageResolver = null
+ this.parent = _parent
+ this.traceManager = _traceManager
+ this.addresses = []
+ this.view
+ this.traceLength
+ this.basicPanel = new DropdownPanel('Full Storages Changes', {json: true})
+ this.init()
+}
+
+FullStoragesChanges.prototype.render = function () {
+ var view = yo`${this.basicPanel.render()}
`
+ if (!this.view) {
+ this.view = view
+ }
+ return view
+}
+
+FullStoragesChanges.prototype.init = function () {
+ var self = this
+ this.parent.event.register('newTraceLoaded', this, function (length) {
+ self.panels = []
+ self.traceManager.getAddresses(function (error, addresses) {
+ if (!error) {
+ self.addresses = addresses
+ self.basicPanel.update({})
+ }
+ })
+
+ self.traceManager.getLength(function (error, length) {
+ if (!error) {
+ self.traceLength = length
+ }
+ })
+ })
+
+ this.parent.event.register('indexChanged', this, function (index) {
+ if (index < 0) return
+ if (self.parent.currentStepIndex !== index) return
+ if (!self.storageResolver) return
+
+ if (index === self.traceLength - 1) {
+ var storageJSON = {}
+ for (var k in self.addresses) {
+ var address = self.addresses[k]
+ var storageViewer = new StorageViewer({
+ stepIndex: self.parent.currentStepIndex,
+ tx: self.parent.tx,
+ address: address
+ }, self.storageResolver, self.traceManager)
+ storageViewer.storageRange(function (error, result) {
+ if (!error) {
+ storageJSON[address] = result
+ self.basicPanel.update(storageJSON)
+ }
+ })
+ }
+ } else {
+ self.basicPanel.update({})
+ }
+ })
+}
+
+module.exports = FullStoragesChanges
diff --git a/src/app/debugger/remix-debugger/src/ui/MemoryPanel.js b/src/app/debugger/remix-debugger/src/ui/MemoryPanel.js
new file mode 100644
index 0000000000..c37cf48b2c
--- /dev/null
+++ b/src/app/debugger/remix-debugger/src/ui/MemoryPanel.js
@@ -0,0 +1,39 @@
+'use strict'
+var DropdownPanel = require('./DropdownPanel')
+var remixLib = require('remix-lib')
+var ui = remixLib.helpers.ui
+var yo = require('yo-yo')
+
+function MemoryPanel (_parent, _traceManager) {
+ this.parent = _parent
+ this.traceManager = _traceManager
+ this.basicPanel = new DropdownPanel('Memory', {
+ json: true,
+ css: {
+ 'font-family': 'monospace'
+ }})
+ this.init()
+}
+
+MemoryPanel.prototype.render = function () {
+ return yo`${this.basicPanel.render()}
`
+}
+
+MemoryPanel.prototype.init = function () {
+ var self = this
+ this.parent.event.register('indexChanged', this, function (index) {
+ if (index < 0) return
+ if (self.parent.currentStepIndex !== index) return
+
+ self.traceManager.getMemoryAt(index, function (error, memory) {
+ if (error) {
+ console.log(error)
+ self.basicPanel.update({})
+ } else if (self.parent.currentStepIndex === index) {
+ self.basicPanel.update(ui.formatMemory(memory, 16))
+ }
+ })
+ })
+}
+
+module.exports = MemoryPanel
diff --git a/src/app/debugger/remix-debugger/src/ui/Slider.js b/src/app/debugger/remix-debugger/src/ui/Slider.js
new file mode 100644
index 0000000000..10ca255d81
--- /dev/null
+++ b/src/app/debugger/remix-debugger/src/ui/Slider.js
@@ -0,0 +1,75 @@
+'use strict'
+var remixLib = require('remix-lib')
+var EventManager = remixLib.EventManager
+var yo = require('yo-yo')
+
+class Slider {
+ constructor (_traceManager, _stepOverride) {
+ this.event = new EventManager()
+ this.traceManager = _traceManager
+ this.max
+ this.disabled = true
+ this.view
+ this.solidityMode = false
+ this.stepOverride = _stepOverride
+
+ this.previousValue = null
+ }
+
+ render () {
+ var self = this
+ var view = yo`
+
+
`
+ if (!this.view) {
+ this.view = view
+ }
+ return view
+ }
+
+ init (length) {
+ var slider = this.view.querySelector('#slider')
+ slider.setAttribute('max', length - 1)
+ this.max = length - 1
+ this.updateDisabled(length === 0)
+ this.disabled = length === 0
+ this.setValue(0)
+ }
+
+ onChange (event) {
+ var value = parseInt(this.view.querySelector('#slider').value)
+ if (this.stepOverride) {
+ var correctedValue = this.stepOverride(value)
+ if (correctedValue !== value) {
+ this.setValue(correctedValue)
+ value = correctedValue
+ }
+ }
+ if (value === this.previousValue) return
+ this.previousValue = value
+ this.event.trigger('moved', [value])
+ }
+
+ setValue (value) {
+ this.view.querySelector('#slider').value = value
+ }
+
+ updateDisabled (disabled) {
+ if (disabled) {
+ this.view.querySelector('#slider').setAttribute('disabled', true)
+ } else {
+ this.view.querySelector('#slider').removeAttribute('disabled')
+ }
+ }
+}
+
+module.exports = Slider
diff --git a/src/app/debugger/remix-debugger/src/ui/SolidityLocals.js b/src/app/debugger/remix-debugger/src/ui/SolidityLocals.js
new file mode 100644
index 0000000000..3b8f6d91e0
--- /dev/null
+++ b/src/app/debugger/remix-debugger/src/ui/SolidityLocals.js
@@ -0,0 +1,80 @@
+'use strict'
+var DropdownPanel = require('./DropdownPanel')
+var remixSolidity = require('remix-solidity')
+var localDecoder = remixSolidity.localDecoder
+var solidityTypeFormatter = require('./SolidityTypeFormatter')
+var remixCore = require('remix-core')
+var StorageViewer = remixCore.storage.StorageViewer
+var yo = require('yo-yo')
+
+class SolidityLocals {
+
+ constructor (_parent, _traceManager, _internalTreeCall) {
+ this.parent = _parent
+ this.internalTreeCall = _internalTreeCall
+ this.storageResolver = null
+ this.traceManager = _traceManager
+ this.basicPanel = new DropdownPanel('Solidity Locals', {
+ json: true,
+ formatSelf: solidityTypeFormatter.formatSelf,
+ extractData: solidityTypeFormatter.extractData
+ })
+ this.init()
+ this.view
+ }
+
+ render () {
+ this.view = yo`
+ ${this.basicPanel.render()}
+
`
+ return this.view
+ }
+
+ init () {
+ var decodeTimeout = null
+ this.parent.event.register('sourceLocationChanged', this, (sourceLocation) => {
+ if (!this.storageResolver) {
+ this.basicPanel.setMessage('storage not ready')
+ return
+ }
+ if (decodeTimeout) {
+ window.clearTimeout(decodeTimeout)
+ }
+ this.basicPanel.setUpdating()
+ decodeTimeout = setTimeout(() => {
+ decode(this, sourceLocation)
+ }, 500)
+ })
+ }
+}
+
+function decode (self, sourceLocation) {
+ self.traceManager.waterfall([
+ self.traceManager.getStackAt,
+ self.traceManager.getMemoryAt,
+ self.traceManager.getCurrentCalledAddressAt],
+ self.parent.currentStepIndex,
+ (error, result) => {
+ if (!error) {
+ var stack = result[0].value
+ var memory = result[1].value
+ try {
+ var storageViewer = new StorageViewer({
+ stepIndex: self.parent.currentStepIndex,
+ tx: self.parent.tx,
+ address: result[2].value
+ }, self.storageResolver, self.traceManager)
+ localDecoder.solidityLocals(self.parent.currentStepIndex, self.internalTreeCall, stack, memory, storageViewer, sourceLocation).then((locals) => {
+ if (!locals.error) {
+ self.basicPanel.update(locals)
+ }
+ })
+ } catch (e) {
+ self.basicPanel.setMessage(e.message)
+ }
+ } else {
+ console.log(error)
+ }
+ })
+}
+module.exports = SolidityLocals
diff --git a/src/app/debugger/remix-debugger/src/ui/SolidityState.js b/src/app/debugger/remix-debugger/src/ui/SolidityState.js
new file mode 100644
index 0000000000..f69fc045f4
--- /dev/null
+++ b/src/app/debugger/remix-debugger/src/ui/SolidityState.js
@@ -0,0 +1,104 @@
+'use strict'
+var DropdownPanel = require('./DropdownPanel')
+var remixSolidity = require('remix-solidity')
+var stateDecoder = remixSolidity.stateDecoder
+var solidityTypeFormatter = require('./SolidityTypeFormatter')
+var remixCore = require('remix-core')
+var StorageViewer = remixCore.storage.StorageViewer
+var yo = require('yo-yo')
+
+function SolidityState (_parent, _traceManager, _codeManager, _solidityProxy) {
+ this.storageResolver = null
+ this.parent = _parent
+ this.traceManager = _traceManager
+ this.codeManager = _codeManager
+ this.solidityProxy = _solidityProxy
+ this.basicPanel = new DropdownPanel('Solidity State', {
+ json: true,
+ formatSelf: solidityTypeFormatter.formatSelf,
+ extractData: solidityTypeFormatter.extractData
+ })
+ this.init()
+ this.view
+ this.stateVariablesByAddresses = {}
+ _parent.event.register('traceUnloaded', () => { this.stateVariablesByAddresses = {} })
+ _parent.event.register('newTraceLoaded', () => { this.stateVariablesByAddresses = {} })
+}
+
+SolidityState.prototype.render = function () {
+ if (!this.view) {
+ this.view = yo`
+ ${this.basicPanel.render()}
+
`
+ }
+ return this.view
+}
+
+SolidityState.prototype.init = function () {
+ var self = this
+ var decodeTimeout = null
+ this.parent.event.register('indexChanged', this, function (index) {
+ if (index < 0) {
+ self.basicPanel.setMessage('invalid step index')
+ return
+ }
+
+ if (self.parent.currentStepIndex !== index) return
+ if (!self.solidityProxy.loaded()) {
+ self.basicPanel.setMessage('no source has been specified')
+ return
+ }
+
+ if (!self.storageResolver) {
+ return
+ }
+ if (decodeTimeout) {
+ window.clearTimeout(decodeTimeout)
+ }
+ self.basicPanel.setUpdating()
+ decodeTimeout = setTimeout(() => {
+ decode(self, index)
+ }, 500)
+ })
+}
+
+function decode (self, index) {
+ self.traceManager.getCurrentCalledAddressAt(self.parent.currentStepIndex, (error, address) => {
+ if (error) {
+ self.basicPanel.update({})
+ console.log(error)
+ } else {
+ if (self.stateVariablesByAddresses[address]) {
+ extractStateVariables(self, self.stateVariablesByAddresses[address], address)
+ } else {
+ self.solidityProxy.extractStateVariablesAt(index, function (error, stateVars) {
+ if (error) {
+ self.basicPanel.update({})
+ console.log(error)
+ } else {
+ self.stateVariablesByAddresses[address] = stateVars
+ extractStateVariables(self, stateVars, address)
+ }
+ })
+ }
+ }
+ })
+}
+
+function extractStateVariables (self, stateVars, address) {
+ var storageViewer = new StorageViewer({
+ stepIndex: self.parent.currentStepIndex,
+ tx: self.parent.tx,
+ address: address
+ }, self.storageResolver, self.traceManager)
+ stateDecoder.decodeState(stateVars, storageViewer).then((result) => {
+ self.basicPanel.setMessage('')
+ if (!result.error) {
+ self.basicPanel.update(result)
+ } else {
+ self.basicPanel.setMessage(result.error)
+ }
+ })
+}
+
+module.exports = SolidityState
diff --git a/src/app/debugger/remix-debugger/src/ui/SolidityTypeFormatter.js b/src/app/debugger/remix-debugger/src/ui/SolidityTypeFormatter.js
new file mode 100644
index 0000000000..48c422418c
--- /dev/null
+++ b/src/app/debugger/remix-debugger/src/ui/SolidityTypeFormatter.js
@@ -0,0 +1,71 @@
+'use strict'
+var yo = require('yo-yo')
+var BN = require('ethereumjs-util').BN
+
+module.exports = {
+ formatSelf: formatSelf,
+ extractData: extractData
+}
+
+function formatSelf (key, data) {
+ var style = fontColor(data)
+ var keyStyle = data.isProperty ? 'color:#847979' : ''
+ if (data.type === 'string') {
+ data.self = JSON.stringify(data.self)
+ }
+ return yo``
+}
+
+function extractData (item, parent, key) {
+ var ret = {}
+ if (item.isProperty) {
+ return item
+ }
+ if (item.type.lastIndexOf(']') === item.type.length - 1) {
+ ret.children = (item.value || []).map(function (item, index) {
+ return {key: index, value: item}
+ })
+ ret.children.unshift({
+ key: 'length',
+ value: {
+ self: (new BN(item.length.replace('0x', ''), 16)).toString(10),
+ type: 'uint',
+ isProperty: true
+ }
+ })
+ ret.isArray = true
+ ret.self = parent.isArray ? '' : item.type
+ } else if (item.type.indexOf('struct') === 0) {
+ ret.children = Object.keys((item.value || {})).map(function (key) {
+ return {key: key, value: item.value[key]}
+ })
+ ret.self = item.type
+ ret.isStruct = true
+ } else if (item.type.indexOf('mapping') === 0) {
+ ret.children = Object.keys((item.value || {})).map(function (key) {
+ return {key: key, value: item.value[key]}
+ })
+ ret.isMapping = true
+ ret.self = item.type
+ } else {
+ ret.children = null
+ ret.self = item.value
+ ret.type = item.type
+ }
+ return ret
+}
+
+function fontColor (data) {
+ var color = '#124B46'
+ if (data.isArray || data.isStruct || data.isMapping) {
+ color = '#847979'
+ } else if (data.type.indexOf('uint') === 0 ||
+ data.type.indexOf('int') === 0 ||
+ data.type.indexOf('bool') === 0 ||
+ data.type.indexOf('enum') === 0) {
+ color = '#0F0CE9'
+ } else if (data.type === 'string') {
+ color = '#E91E0C'
+ }
+ return 'color:' + color
+}
diff --git a/src/app/debugger/remix-debugger/src/ui/StackPanel.js b/src/app/debugger/remix-debugger/src/ui/StackPanel.js
new file mode 100644
index 0000000000..c07aa01094
--- /dev/null
+++ b/src/app/debugger/remix-debugger/src/ui/StackPanel.js
@@ -0,0 +1,44 @@
+'use strict'
+var DropdownPanel = require('./DropdownPanel')
+var remixLib = require('remix-lib')
+var ui = remixLib.helpers.ui
+var yo = require('yo-yo')
+
+function StackPanel (_parent, _traceManager) {
+ this.parent = _parent
+ this.traceManager = _traceManager
+ this.basicPanel = new DropdownPanel('Stack', {json: true})
+ this.init()
+}
+
+StackPanel.prototype.render = function () {
+ return yo`${this.basicPanel.render()}
`
+}
+
+StackPanel.prototype.init = function () {
+ var self = this
+ this.parent.event.register('indexChanged', this, function (index) {
+ if (index < 0) return
+ if (self.parent.currentStepIndex !== index) return
+
+ self.traceManager.getStackAt(index, function (error, stack) {
+ if (error) {
+ self.basicPanel.update({})
+ console.log(error)
+ } else if (self.parent.currentStepIndex === index) {
+ self.basicPanel.update(self.format(stack))
+ }
+ })
+ })
+}
+
+StackPanel.prototype.format = function (stack) {
+ var ret = []
+ stack.map(function (item, i) {
+ var hex = ui.normalizeHex(item)
+ ret.push(hex)
+ })
+ return ret
+}
+
+module.exports = StackPanel
diff --git a/src/app/debugger/remix-debugger/src/ui/StepDetail.js b/src/app/debugger/remix-debugger/src/ui/StepDetail.js
new file mode 100644
index 0000000000..88950e4e93
--- /dev/null
+++ b/src/app/debugger/remix-debugger/src/ui/StepDetail.js
@@ -0,0 +1,100 @@
+'use strict'
+var yo = require('yo-yo')
+var DropdownPanel = require('./DropdownPanel')
+
+function StepDetail (_parent, _traceManager) {
+ this.parent = _parent
+ this.traceManager = _traceManager
+
+ this.basicPanel = new DropdownPanel('Step detail', {json: true})
+
+ this.detail = initDetail()
+ this.view
+ this.init()
+}
+
+StepDetail.prototype.render = function () {
+ return yo`${this.basicPanel.render()}
`
+}
+
+StepDetail.prototype.init = function () {
+ var self = this
+ this.parent.event.register('traceUnloaded', this, function () {
+ self.detail = initDetail()
+ self.basicPanel.update(self.detail)
+ })
+
+ this.parent.event.register('newTraceLoaded', this, function () {
+ self.detail = initDetail()
+ self.basicPanel.update(self.detail)
+ })
+
+ this.parent.event.register('indexChanged', this, function (index) {
+ if (index < 0) return
+
+ self.detail['vm trace step'] = index
+
+ self.traceManager.getCurrentStep(index, function (error, step) {
+ if (error) {
+ console.log(error)
+ self.detail['execution step'] = '-'
+ } else {
+ self.detail['execution step'] = step
+ }
+ self.basicPanel.update(self.detail)
+ })
+
+ self.traceManager.getMemExpand(index, function (error, addmem) {
+ if (error) {
+ console.log(error)
+ self.detail['add memory'] = '-'
+ } else {
+ self.detail['add memory'] = addmem
+ }
+ self.basicPanel.update(self.detail)
+ })
+
+ self.traceManager.getStepCost(index, function (error, gas) {
+ if (error) {
+ console.log(error)
+ self.detail.gas = '-'
+ } else {
+ self.detail.gas = gas
+ }
+ self.basicPanel.update(self.detail)
+ })
+
+ self.traceManager.getCurrentCalledAddressAt(index, function (error, address) {
+ if (error) {
+ console.log(error)
+ self.detail['loaded address'] = '-'
+ } else {
+ self.detail['loaded address'] = address
+ }
+ self.basicPanel.update(self.detail)
+ })
+
+ self.traceManager.getRemainingGas(index, function (error, remaingas) {
+ if (error) {
+ console.log(error)
+ self.detail['remaining gas'] = '-'
+ } else {
+ self.detail['remaining gas'] = remaingas
+ }
+ self.basicPanel.update(self.detail)
+ })
+ })
+}
+
+module.exports = StepDetail
+
+function initDetail () {
+ return {
+ 'vm trace step': '-',
+ 'execution step': '-',
+ 'add memory': '',
+ 'gas': '',
+ 'remaining gas': '-',
+ 'loaded address': '-'
+ }
+}
diff --git a/src/app/debugger/remix-debugger/src/ui/StepManager.js b/src/app/debugger/remix-debugger/src/ui/StepManager.js
new file mode 100644
index 0000000000..eaa2ec0d85
--- /dev/null
+++ b/src/app/debugger/remix-debugger/src/ui/StepManager.js
@@ -0,0 +1,204 @@
+'use strict'
+var ButtonNavigator = require('./ButtonNavigator')
+var Slider = require('./Slider')
+var remixLib = require('remix-lib')
+var EventManager = remixLib.EventManager
+var yo = require('yo-yo')
+var util = remixLib.util
+
+function StepManager (_parent, _traceManager) {
+ this.event = new EventManager()
+ this.parent = _parent
+ this.traceManager = _traceManager
+ this.sourceMapByAddress = {}
+ this.solidityMode = false
+
+ var self = this
+ this.parent.event.register('newTraceLoaded', this, function () {
+ self.traceManager.getLength(function (error, length) {
+ if (error) {
+ console.log(error)
+ } else {
+ self.slider.init(length)
+ self.init()
+ }
+ })
+ })
+
+ this.slider = new Slider(this.traceManager, (step) => {
+ return this.solidityMode ? this.resolveToReducedTrace(step, 0) : step
+ })
+ this.slider.event.register('moved', this, function (step) {
+ self.sliderMoved(step)
+ })
+
+ this.parent.callTree.event.register('callTreeReady', () => {
+ this.solidityMode = true
+ this.parent.vmDebugger.asmCode.event.register('hide', () => {
+ this.solidityMode = this.parent.callTree.reducedTrace.length !== 0
+ })
+ this.parent.vmDebugger.asmCode.event.register('show', () => {
+ this.solidityMode = false
+ })
+ if (this.parent.callTree.functionCallStack.length) {
+ this.jumpTo(this.parent.callTree.functionCallStack[0])
+ }
+ })
+
+ this.buttonNavigator = new ButtonNavigator(_parent, this.traceManager)
+ this.buttonNavigator.event.register('stepIntoBack', this, function () {
+ self.stepIntoBack()
+ })
+ this.buttonNavigator.event.register('stepIntoForward', this, function () {
+ self.stepIntoForward()
+ })
+ this.buttonNavigator.event.register('stepOverBack', this, function () {
+ self.stepOverBack()
+ })
+ this.buttonNavigator.event.register('stepOverForward', this, function () {
+ self.stepOverForward()
+ })
+ this.buttonNavigator.event.register('jumpOut', this, function () {
+ self.jumpOut()
+ })
+ this.buttonNavigator.event.register('jumpToException', this, function (exceptionIndex) {
+ self.jumpTo(exceptionIndex)
+ })
+ this.buttonNavigator.event.register('jumpNextBreakpoint', (exceptionIndex) => {
+ self.parent.breakpointManager.jumpNextBreakpoint(true)
+ })
+ this.buttonNavigator.event.register('jumpPreviousBreakpoint', (exceptionIndex) => {
+ self.parent.breakpointManager.jumpPreviousBreakpoint(true)
+ })
+}
+
+StepManager.prototype.resolveToReducedTrace = function (value, incr) {
+ if (this.parent.callTree.reducedTrace.length) {
+ var nextSource = util.findClosestIndex(value, this.parent.callTree.reducedTrace)
+ nextSource = nextSource + incr
+ if (nextSource <= 0) {
+ nextSource = 0
+ } else if (nextSource > this.parent.callTree.reducedTrace.length) {
+ nextSource = this.parent.callTree.reducedTrace.length - 1
+ }
+ return this.parent.callTree.reducedTrace[nextSource]
+ }
+ return value
+}
+
+StepManager.prototype.render = function () {
+ return (
+ yo`
+ ${this.slider.render()}
+ ${this.buttonNavigator.render()}
+
`
+ )
+}
+
+StepManager.prototype.reset = function () {
+ this.slider.setValue(0)
+ this.currentStepIndex = 0
+ this.buttonNavigator.reset()
+}
+
+StepManager.prototype.init = function () {
+ this.slider.setValue(0)
+ this.changeState(0)
+}
+
+StepManager.prototype.newTraceAvailable = function () {
+ this.init()
+}
+
+StepManager.prototype.jumpTo = function (step) {
+ if (!this.traceManager.inRange(step)) {
+ return
+ }
+ this.slider.setValue(step)
+ this.changeState(step)
+}
+
+StepManager.prototype.sliderMoved = function (step) {
+ if (!this.traceManager.inRange(step)) {
+ return
+ }
+ this.changeState(step)
+}
+
+StepManager.prototype.stepIntoForward = function () {
+ if (!this.traceManager.isLoaded()) {
+ return
+ }
+ var step = this.currentStepIndex
+ if (this.solidityMode) {
+ step = this.resolveToReducedTrace(step, 1)
+ } else {
+ step += 1
+ }
+ if (!this.traceManager.inRange(step)) {
+ return
+ }
+ this.slider.setValue(step)
+ this.changeState(step)
+}
+
+StepManager.prototype.stepIntoBack = function () {
+ if (!this.traceManager.isLoaded()) {
+ return
+ }
+ var step = this.currentStepIndex
+ if (this.solidityMode) {
+ step = this.resolveToReducedTrace(step, -1)
+ } else {
+ step -= 1
+ }
+ if (!this.traceManager.inRange(step)) {
+ return
+ }
+ this.slider.setValue(step)
+ this.changeState(step)
+}
+
+StepManager.prototype.stepOverForward = function () {
+ if (!this.traceManager.isLoaded()) {
+ return
+ }
+ var step = this.traceManager.findStepOverForward(this.currentStepIndex)
+ if (this.solidityMode) {
+ step = this.resolveToReducedTrace(step, 1)
+ }
+ this.slider.setValue(step)
+ this.changeState(step)
+}
+
+StepManager.prototype.stepOverBack = function () {
+ if (!this.traceManager.isLoaded()) {
+ return
+ }
+ var step = this.traceManager.findStepOverBack(this.currentStepIndex)
+ if (this.solidityMode) {
+ step = this.resolveToReducedTrace(step, -1)
+ }
+ this.slider.setValue(step)
+ this.changeState(step)
+}
+
+StepManager.prototype.jumpOut = function () {
+ if (!this.traceManager.isLoaded()) {
+ return
+ }
+ var step = this.traceManager.findStepOut(this.currentStepIndex)
+ if (this.solidityMode) {
+ step = this.resolveToReducedTrace(step, 0)
+ }
+ this.slider.setValue(step)
+ this.changeState(step)
+}
+
+StepManager.prototype.changeState = function (step) {
+ this.currentStepIndex = step
+ this.buttonNavigator.stepChanged(step)
+ this.event.trigger('stepChanged', [step])
+}
+
+module.exports = StepManager
diff --git a/src/app/debugger/remix-debugger/src/ui/StoragePanel.js b/src/app/debugger/remix-debugger/src/ui/StoragePanel.js
new file mode 100644
index 0000000000..cd4573e4f7
--- /dev/null
+++ b/src/app/debugger/remix-debugger/src/ui/StoragePanel.js
@@ -0,0 +1,50 @@
+'use strict'
+var DropdownPanel = require('./DropdownPanel')
+var remixCore = require('remix-core')
+var StorageViewer = remixCore.storage.StorageViewer
+var yo = require('yo-yo')
+
+function StoragePanel (_parent, _traceManager) {
+ this.parent = _parent
+ this.storageResolver = null
+ this.traceManager = _traceManager
+ this.basicPanel = new DropdownPanel('Storage', {json: true})
+ this.init()
+ this.disabled = false
+}
+
+StoragePanel.prototype.render = function () {
+ return yo`${this.basicPanel.render()}
`
+}
+
+StoragePanel.prototype.init = function () {
+ var self = this
+ this.parent.event.register('indexChanged', this, function (index) {
+ if (self.disabled) return
+ if (index < 0) return
+ if (self.parent.currentStepIndex !== index) return
+ if (!self.storageResolver) return
+
+ this.traceManager.getCurrentCalledAddressAt(index, (error, address) => {
+ if (!error) {
+ var storageViewer = new StorageViewer({
+ stepIndex: self.parent.currentStepIndex,
+ tx: self.parent.tx,
+ address: address
+ }, self.storageResolver, self.traceManager)
+
+ storageViewer.storageRange((error, storage) => {
+ if (error) {
+ console.log(error)
+ self.basicPanel.update({})
+ } else if (self.parent.currentStepIndex === index) {
+ var header = storageViewer.isComplete(address) ? 'completely loaded' : 'partially loaded...'
+ self.basicPanel.update(storage, header)
+ }
+ })
+ }
+ })
+ })
+}
+
+module.exports = StoragePanel
diff --git a/src/app/debugger/remix-debugger/src/ui/TreeView.js b/src/app/debugger/remix-debugger/src/ui/TreeView.js
new file mode 100644
index 0000000000..7601a707f3
--- /dev/null
+++ b/src/app/debugger/remix-debugger/src/ui/TreeView.js
@@ -0,0 +1,186 @@
+'use strict'
+var yo = require('yo-yo')
+var csjs = require('csjs-inject')
+var css = csjs`
+ .li_tv {
+ list-style-type: none;
+ -webkit-margin-before: 0px;
+ -webkit-margin-after: 0px;
+ -webkit-margin-start: 0px;
+ -webkit-margin-end: 0px;
+ -webkit-padding-start: 0px;
+ margin-left: 10px;
+ }
+ .ul_tv {
+ list-style-type: none;
+ -webkit-margin-before: 0px;
+ -webkit-margin-after: 0px;
+ -webkit-margin-start: 0px;
+ -webkit-margin-end: 0px;
+ -webkit-padding-start: 0px;
+ }
+ .caret_tv {
+ width: 10px;
+ }
+ .label_tv {
+ display: flex;
+ align-items: center;
+ }
+`
+
+var remixLib = require('remix-lib')
+var EventManager = remixLib.EventManager
+
+/**
+ * TreeView
+ * - extendable by specifying custom `extractData` and `formatSelf` function
+ * - trigger `nodeClick` and `leafClick`
+ */
+class TreeView {
+
+ constructor (opts) {
+ this.event = new EventManager()
+ this.extractData = opts.extractData || this.extractDataDefault
+ this.formatSelf = opts.formatSelf || this.formatSelfDefault
+ this.view = null
+ }
+
+ render (json, expand) {
+ var view = this.renderProperties(json, expand)
+ if (!this.view) {
+ this.view = view
+ }
+ return view
+ }
+
+ update (json) {
+ if (this.view) {
+ yo.update(this.view, this.render(json))
+ }
+ }
+
+ renderObject (item, parent, key, expand, keyPath) {
+ var data = this.extractData(item, parent, key)
+ var children = (data.children || []).map((child, index) => {
+ return this.renderObject(child.value, data, child.key, expand, keyPath + '/' + child.key)
+ })
+ return this.formatData(key, data, children, expand, keyPath)
+ }
+
+ renderProperties (json, expand, key) {
+ key = key || ''
+ var children = Object.keys(json).map((innerkey) => {
+ return this.renderObject(json[innerkey], json, innerkey, expand, innerkey)
+ })
+ return yo``
+ }
+
+ formatData (key, data, children, expand, keyPath) {
+ var self = this
+ var li = yo``
+ var caret = yo``
+ var label = yo`
+
+ ${caret}
+ ${self.formatSelf(key, data, li)}
+
`
+ li.appendChild(label)
+ if (data.children) {
+ var list = yo``
+ list.style.display = 'none'
+ caret.className = list.style.display === 'none' ? `fa fa-caret-right caret ${css.caret_tv}` : `fa fa-caret-down caret ${css.caret_tv}`
+ label.onclick = function () {
+ self.expand(keyPath)
+ }
+ label.oncontextmenu = function (event) {
+ self.event.trigger('nodeRightClick', [keyPath, data, label, event])
+ }
+ li.appendChild(list)
+ } else {
+ caret.style.visibility = 'hidden'
+ label.oncontextmenu = function (event) {
+ self.event.trigger('leafRightClick', [keyPath, data, label, event])
+ }
+ label.onclick = function (event) {
+ self.event.trigger('leafClick', [keyPath, data, label, event])
+ }
+ }
+ return li
+ }
+
+ isExpanded (path) {
+ var current = this.nodeAt(path)
+ if (current) {
+ return current.style.display !== 'none'
+ }
+ return false
+ }
+
+ expand (path) {
+ var caret = this.caretAt(path)
+ var node = this.nodeAt(path)
+ if (node) {
+ node.style.display = node.style.display === 'none' ? 'block' : 'none'
+ caret.className = node.style.display === 'none' ? `fa fa-caret-right caret ${css.caret_tv}` : `fa fa-caret-down caret ${css.caret_tv}`
+ this.event.trigger('nodeClick', [path, node])
+ }
+ }
+
+ caretAt (path) {
+ var label = this.labelAt(path)
+ if (label) {
+ return label.querySelector('.caret')
+ }
+ }
+
+ itemAt (path) {
+ return this.view.querySelector(`li[key="${path}"]`)
+ }
+
+ labelAt (path) {
+ return this.view.querySelector(`div[key="${path}"]`)
+ }
+
+ nodeAt (path) {
+ return this.view.querySelector(`ul[key="${path}"]`)
+ }
+
+ updateNodeFromJSON (path, jsonTree, expand) {
+ var newTree = this.renderProperties(jsonTree, expand, path)
+ var current = this.nodeAt(path)
+ if (current && current.parentElement) {
+ current.parentElement.replaceChild(newTree, current)
+ }
+ }
+
+ formatSelfDefault (key, data) {
+ return yo`${key}: ${data.self}`
+ }
+
+ extractDataDefault (item, parent, key) {
+ var ret = {}
+ if (item instanceof Array) {
+ ret.children = item.map((item, index) => {
+ return {key: index, value: item}
+ })
+ ret.self = 'Array'
+ ret.isNode = true
+ ret.isLeaf = false
+ } else if (item instanceof Object) {
+ ret.children = Object.keys(item).map((key) => {
+ return {key: key, value: item[key]}
+ })
+ ret.self = 'Object'
+ ret.isNode = true
+ ret.isLeaf = false
+ } else {
+ ret.self = item
+ ret.children = null
+ ret.isNode = false
+ ret.isLeaf = true
+ }
+ return ret
+ }
+}
+
+module.exports = TreeView
diff --git a/src/app/debugger/remix-debugger/src/ui/TxBrowser.js b/src/app/debugger/remix-debugger/src/ui/TxBrowser.js
new file mode 100644
index 0000000000..2d90132d26
--- /dev/null
+++ b/src/app/debugger/remix-debugger/src/ui/TxBrowser.js
@@ -0,0 +1,211 @@
+var remixLib = require('remix-lib')
+var global = remixLib.global
+var EventManager = remixLib.EventManager
+var traceHelper = remixLib.helpers.trace
+var yo = require('yo-yo')
+var init = remixLib.init
+var DropdownPanel = require('./DropdownPanel')
+var csjs = require('csjs-inject')
+var styleGuide = remixLib.ui.themeChooser
+var styles = styleGuide.chooser()
+
+var css = csjs`
+ .container {
+ display: flex;
+ flex-direction: column;
+ }
+ .txContainer {
+ display: flex;
+ flex-direction: column;
+ }
+ .txinputs {
+ width: 100%;
+ display: flex;
+ justify-content: center;
+ }
+ .txinput {
+ ${styles.rightPanel.debuggerTab.input_Debugger}
+ min-width: 30px;
+ margin: 3px;
+ }
+ .txbuttons {
+ width: 100%;
+ display: flex;
+ justify-content: center;
+ }
+ .txbutton {
+ ${styles.rightPanel.debuggerTab.button_Debugger}
+ }
+ .txbutton:hover {
+ color: ${styles.rightPanel.debuggerTab.button_Debugger_icon_HoverColor};
+ }
+ .txinfo {
+ margin-top: 5px;
+ }
+ .vmargin {
+ margin-top: 10px;
+ margin-bottom: 10px;
+ }
+`
+function TxBrowser (_parent) {
+ this.event = new EventManager()
+
+ this.blockNumber
+ this.txNumber
+ this.view
+ this.displayConnectionSetting = true
+ this.basicPanel = new DropdownPanel('Transaction', {json: true})
+ this.basicPanel.data = {}
+ var self = this
+ _parent.event.register('providerChanged', this, function (provider) {
+ self.displayConnectionSetting = provider === 'INTERNAL'
+ self.setDefaultValues()
+ if (self.view) {
+ yo.update(self.view, self.render())
+ }
+ })
+}
+
+// creation 0xa9619e1d0a35b2c1d686f5b661b3abd87f998d2844e8e9cc905edb57fc9ce349
+// invokation 0x71a6d583d16d142c5c3e8903060e8a4ee5a5016348a9448df6c3e63b68076ec4 0xcda2b2835add61af54cf83bd076664d98d7908c6cd98d86423b3b48d8b8e51ff
+// test:
+// creation: 0x72908de76f99fca476f9e3a3b5d352f350a98cd77d09cebfc59ffe32a6ecaa0b
+// invokation: 0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd51
+
+TxBrowser.prototype.setDefaultValues = function () {
+ this.connectInfo = ''
+ this.basicPanel.update({})
+ this.basicPanel.hide()
+ if (this.view) {
+ yo.update(this.view, this.render())
+ }
+}
+
+TxBrowser.prototype.submit = function () {
+ if (!this.txNumber) {
+ return
+ }
+ this.event.trigger('newTxLoading', [this.blockNumber, this.txNumber])
+ try {
+ var self = this
+ if (this.txNumber.indexOf('0x') !== -1) {
+ global.web3.eth.getTransaction(this.txNumber, function (error, result) {
+ self.update(error, result)
+ })
+ } else {
+ global.web3.eth.getTransactionFromBlock(this.blockNumber, this.txNumber, function (error, result) {
+ self.update(error, result)
+ })
+ }
+ } catch (e) {
+ self.update(e.message)
+ }
+}
+
+TxBrowser.prototype.update = function (error, tx) {
+ var info = {}
+ if (error) {
+ this.view.querySelector('#error').innerHTML = error
+ } else {
+ if (tx) {
+ this.view.querySelector('#error').innerHTML = ''
+ if (!tx.to) {
+ tx.to = traceHelper.contractCreationToken('0')
+ }
+ info.from = tx.from
+ info.to = tx.to
+ info.hash = tx.hash
+ this.event.trigger('newTraceRequested', [this.blockNumber, this.txNumber, tx])
+ } else {
+ var mes = ''
+ info.from = mes
+ info.to = mes
+ info.hash = mes
+ this.view.querySelector('#error').innerHTML = 'Cannot find transaction with reference. Block number: ' + this.blockNumber + '. Transaction index/hash: ' + this.txNumber
+ }
+ }
+ this.basicPanel.update(info)
+}
+
+TxBrowser.prototype.updateWeb3Url = function (newhost) {
+ init.setProvider(global.web3, newhost)
+ var self = this
+ this.checkWeb3(function (error, block) {
+ if (!error) {
+ self.connectInfo = 'Connected to ' + global.web3.currentProvider.host + '. Current block number: ' + block
+ } else {
+ self.connectInfo = 'Unable to connect to ' + global.web3.currentProvider.host + '. ' + error.message
+ }
+ yo.update(self.view, self.render())
+ })
+}
+
+TxBrowser.prototype.checkWeb3 = function (callback) {
+ try {
+ global.web3.eth.getBlockNumber(function (error, block) {
+ callback(error, block)
+ })
+ } catch (e) {
+ console.log(e)
+ callback(e.message, null)
+ }
+}
+
+TxBrowser.prototype.updateBlockN = function (ev) {
+ this.blockNumber = ev.target.value
+}
+
+TxBrowser.prototype.updateTxN = function (ev) {
+ this.txNumber = ev.target.value
+}
+
+TxBrowser.prototype.load = function (txHash) {
+ this.txNumber = txHash
+ this.submit()
+}
+
+TxBrowser.prototype.unload = function (txHash) {
+ this.event.trigger('unloadRequested')
+ this.init()
+}
+
+TxBrowser.prototype.init = function (ev) {
+ this.setDefaultValues()
+}
+
+TxBrowser.prototype.connectionSetting = function () {
+ if (this.displayConnectionSetting) {
+ var self = this
+ return yo`Node URL:
+ ${this.connectInfo}
`
+ } else {
+ return ''
+ }
+}
+
+TxBrowser.prototype.render = function () {
+ var self = this
+ var view = yo`
+ ${this.connectionSetting()}
+
+
+
+ ${this.basicPanel.render()}
+
+
`
+ if (!this.view) {
+ this.view = view
+ }
+ return view
+}
+
+module.exports = TxBrowser
diff --git a/src/app/debugger/remix-debugger/src/ui/VmDebugger.js b/src/app/debugger/remix-debugger/src/ui/VmDebugger.js
new file mode 100644
index 0000000000..c372c7b9f5
--- /dev/null
+++ b/src/app/debugger/remix-debugger/src/ui/VmDebugger.js
@@ -0,0 +1,90 @@
+'use strict'
+var CodeListView = require('./CodeListView')
+var CalldataPanel = require('./CalldataPanel')
+var MemoryPanel = require('./MemoryPanel')
+var CallstackPanel = require('./CallstackPanel')
+var StackPanel = require('./StackPanel')
+var StoragePanel = require('./StoragePanel')
+var FullStoragesChangesPanel = require('./FullStoragesChanges')
+var StepDetail = require('./StepDetail')
+var DropdownPanel = require('./DropdownPanel')
+var SolidityState = require('./SolidityState')
+var SolidityLocals = require('./SolidityLocals')
+var remixCore = require('remix-core')
+var StorageResolver = remixCore.storage.StorageResolver
+var yo = require('yo-yo')
+
+function VmDebugger (_parent, _traceManager, _codeManager, _solidityProxy, _callTree) {
+ this.asmCode = new CodeListView(_parent, _codeManager)
+ this.stackPanel = new StackPanel(_parent, _traceManager)
+ this.storagePanel = new StoragePanel(_parent, _traceManager)
+ this.memoryPanel = new MemoryPanel(_parent, _traceManager)
+ this.calldataPanel = new CalldataPanel(_parent, _traceManager)
+ this.callstackPanel = new CallstackPanel(_parent, _traceManager)
+ this.stepDetail = new StepDetail(_parent, _traceManager)
+ this.solidityState = new SolidityState(_parent, _traceManager, _codeManager, _solidityProxy)
+ this.solidityLocals = new SolidityLocals(_parent, _traceManager, _callTree)
+
+ /* Return values - */
+ this.returnValuesPanel = new DropdownPanel('Return Value', {json: true})
+ this.returnValuesPanel.data = {}
+ _parent.event.register('indexChanged', this.returnValuesPanel, function (index) {
+ var self = this
+ _traceManager.getReturnValue(index, function (error, returnValue) {
+ if (error) {
+ self.update([error])
+ } else if (_parent.currentStepIndex === index) {
+ self.update([returnValue])
+ }
+ })
+ })
+ /* Return values - */
+
+ this.fullStoragesChangesPanel = new FullStoragesChangesPanel(_parent, _traceManager)
+
+ this.view
+ var self = this
+ _parent.event.register('newTraceLoaded', this, function () {
+ var storageResolver = new StorageResolver()
+ self.storagePanel.storageResolver = storageResolver
+ self.solidityState.storageResolver = storageResolver
+ self.solidityLocals.storageResolver = storageResolver
+ self.fullStoragesChangesPanel.storageResolver = storageResolver
+ self.view.style.display = 'block'
+ })
+ _parent.event.register('traceUnloaded', this, function () {
+ self.view.style.display = 'none'
+ })
+ _parent.callTree.event.register('callTreeReady', () => {
+ if (_parent.callTree.reducedTrace.length) {
+ self.solidityLocals.basicPanel.show()
+ self.solidityState.basicPanel.show()
+ } else {
+ self.asmCode.basicPanel.show()
+ }
+ })
+}
+
+VmDebugger.prototype.render = function () {
+ var view = yo`
+
+ ${this.asmCode.render()}
+ ${this.solidityLocals.render()}
+ ${this.solidityState.render()}
+ ${this.stepDetail.render()}
+ ${this.stackPanel.render()}
+ ${this.storagePanel.render()}
+ ${this.memoryPanel.render()}
+ ${this.calldataPanel.render()}
+ ${this.callstackPanel.render()}
+ ${this.returnValuesPanel.render()}
+ ${this.fullStoragesChangesPanel.render()}
+
+
`
+ if (!this.view) {
+ this.view = view
+ }
+ return view
+}
+
+module.exports = VmDebugger
diff --git a/src/app/debugger/remix-debugger/src/ui/styles/basicStyles.js b/src/app/debugger/remix-debugger/src/ui/styles/basicStyles.js
new file mode 100644
index 0000000000..1517f06c8d
--- /dev/null
+++ b/src/app/debugger/remix-debugger/src/ui/styles/basicStyles.js
@@ -0,0 +1,84 @@
+'use strict'
+module.exports = {
+ truncate: {
+ 'white-space': 'nowrap',
+ 'overflow': 'hidden',
+ 'text-overflow': 'ellipsis',
+ 'margin-right': '5px'
+ },
+ font: {
+ 'font-family': 'arial,sans-serif'
+ },
+ innerShift: {
+ 'padding': '2px',
+ 'margin-left': '10px'
+ },
+ container: {
+ 'margin': '10px',
+ 'padding': '5px'
+ },
+ statusMessage: {
+ 'margin-left': '15px'
+ },
+ address: {
+ 'font-style': 'italic'
+ },
+ instructionsList: {
+ 'width': '52%',
+ 'overflow-y': 'scroll',
+ 'max-height': '250px',
+ 'margin': '0',
+ 'margin-left': '10px',
+ 'padding': '2px'
+ },
+ transactionInfo: {
+ 'margin-top': '5px'
+ },
+ panel: {
+ container: {
+ 'border': '1px solid',
+ 'width': '70%'
+ },
+ tableContainer: {
+ 'height': '50%',
+ 'overflow-y': 'auto'
+ },
+ table: {
+ 'padding': '5px'
+ },
+ title: {
+ 'padding': '5px',
+ 'font-style': 'italic'
+ }
+ },
+ hidden: {
+ 'display': 'none'
+ },
+ display: {
+ 'display': 'block'
+ },
+ inline: {
+ 'display': 'inline-block'
+ },
+ vmargin: {
+ 'margin-top': '10px',
+ 'margin-bottom': '10px'
+ },
+ button: {
+ 'border-color': 'transparent',
+ 'border-radius': '3px',
+ 'border': '.3px solid hsla(0, 0%, 40%, 1)',
+ 'cursor': 'pointer',
+ 'min-height': '25px',
+ 'max-height': '25px',
+ 'padding': '3px',
+ 'min-width': '100px',
+ 'font-size': '12px',
+ 'overflow': 'hidden',
+ 'word-break': 'normal',
+ 'background-color': 'hsla(0, 0%, 40%, .2)',
+ 'color': 'hsla(0, 0%, 40%, 1)',
+ 'margin': '3px',
+ 'text-decoration': 'none'
+ }
+}
diff --git a/src/app/debugger/remix-debugger/src/ui/styles/dropdownPanel.js b/src/app/debugger/remix-debugger/src/ui/styles/dropdownPanel.js
new file mode 100644
index 0000000000..bb9a1e7f2b
--- /dev/null
+++ b/src/app/debugger/remix-debugger/src/ui/styles/dropdownPanel.js
@@ -0,0 +1,31 @@
+'use strict'
+module.exports = {
+ title: {
+ 'border': '1px solid #dadada',
+ 'background-color': 'white',
+ 'width': '100%',
+ 'color': '#363f47',
+ 'margin-top': '5px',
+ 'cursor': 'pointer'
+ },
+ titleInner: {
+ 'display': 'inline-block'
+ },
+ content: {
+ 'color': '#111111',
+ 'width': '100%',
+ 'min-height': '20px'
+ },
+ inner: {
+ 'padding': '2px',
+ 'word-break': 'break-all'
+ },
+ copyBtn: {
+ 'float': 'right',
+ 'margin-top': '3px'
+ },
+ caret: {
+ 'margin-left': '10px',
+ 'margin-right': '10px'
+ }
+}
diff --git a/src/app/debugger/remix-debugger/src/ui/styles/sliderStyles.js b/src/app/debugger/remix-debugger/src/ui/styles/sliderStyles.js
new file mode 100644
index 0000000000..45912b6380
--- /dev/null
+++ b/src/app/debugger/remix-debugger/src/ui/styles/sliderStyles.js
@@ -0,0 +1,6 @@
+'use strict'
+module.exports = {
+ rule: {
+ 'width': '100%'
+ }
+}
diff --git a/src/app/debugger/remix-debugger/src/ui/styles/treeView.js b/src/app/debugger/remix-debugger/src/ui/styles/treeView.js
new file mode 100644
index 0000000000..b50bacd24f
--- /dev/null
+++ b/src/app/debugger/remix-debugger/src/ui/styles/treeView.js
@@ -0,0 +1,30 @@
+'use strict'
+module.exports = {
+ cssUl: {
+ 'list-style-type': 'none',
+ '-webkit-margin-before': '0px',
+ '-webkit-margin-after': '0px',
+ '-webkit-margin-start': '0px',
+ '-webkit-margin-end': '0px',
+ '-webkit-padding-start': '0px'
+ },
+ cssLi: {
+ 'list-style-type': 'none',
+ '-webkit-margin-before': '0px',
+ '-webkit-margin-after': '0px',
+ '-webkit-margin-start': '0px',
+ '-webkit-margin-end': '0px',
+ '-webkit-padding-start': '0px',
+ 'margin-left': '10px'
+ },
+ label: {
+ 'vertical-align': 'top',
+ 'font-family': 'arial,sans-serif'
+ },
+ caret: {
+ 'margin-top': '3px',
+ 'width': '10px'
+ },
+ data: {
+ }
+}
diff --git a/src/app/debugger/remix-debugger/test-browser/resources/insertTestWeb3.js b/src/app/debugger/remix-debugger/test-browser/resources/insertTestWeb3.js
new file mode 100644
index 0000000000..833c7198e1
--- /dev/null
+++ b/src/app/debugger/remix-debugger/test-browser/resources/insertTestWeb3.js
@@ -0,0 +1,77 @@
+/* global XMLHttpRequest */
+function loadJSON (url, callback) {
+ var xobj = new XMLHttpRequest()
+ xobj.overrideMimeType('application/json')
+ xobj.open('GET', url, true)
+ xobj.onreadystatechange = function () {
+ if (xobj.readyState === 4 && xobj.status === 200) {
+ callback(xobj.responseText)
+ }
+ }
+ xobj.send(null)
+}
+
+function loadTestWeb3 (data) {
+ var container = document.getElementById('app')
+ var vmdebugger = container.debugger
+ var uiTestweb3 = {}
+ uiTestweb3.eth = {}
+ uiTestweb3.debug = {}
+ uiTestweb3.eth.getCode = function (address, callback) {
+ if (callback) {
+ callback(null, data.testCodes[address])
+ } else {
+ return data.testCodes[address]
+ }
+ }
+
+ uiTestweb3.debug.traceTransaction = function (txHash, options, callback) {
+ callback(null, data.testTraces[txHash])
+ }
+
+ uiTestweb3.debug.storageRangeAt = function (blockNumber, txIndex, address, start, size, callback) {
+ callback(null, { storage: {}, complete: true })
+ }
+
+ uiTestweb3.eth.getTransaction = function (txHash, callback) {
+ if (callback) {
+ callback(null, data.testTxs[txHash])
+ } else {
+ return data.testTxs[txHash]
+ }
+ }
+
+ uiTestweb3.eth.getTransactionFromBlock = function (blockNumber, txIndex, callback) {
+ if (callback) {
+ callback(null, data.testTxsByBlock[blockNumber + '-' + txIndex])
+ } else {
+ return data.testTxsByBlock[blockNumber + '-' + txIndex]
+ }
+ }
+
+ uiTestweb3.eth.getBlockNumber = function (callback) { callback(null, 'web3 modified for testing purposes :)') }
+
+ uiTestweb3.providers = { 'HttpProvider': function (url) {} }
+
+ uiTestweb3.setProvider = function (provider) {}
+
+ uiTestweb3.currentProvider = {host: 'web3 modified for testing purposes :)'}
+ vmdebugger.addProvider('TEST', uiTestweb3)
+ vmdebugger.switchProvider('TEST')
+}
+
+function waitForRemix (data) {
+ setTimeout(function () {
+ if (!document.getElementById('app').debugger) {
+ waitForRemix(data)
+ } else {
+ loadTestWeb3(data)
+ }
+ }, 500)
+}
+
+loadJSON('/test-browser/resources/testWeb3.json', function (result) {
+ var data = JSON.parse(result)
+ waitForRemix(data)
+})
+
diff --git a/src/app/debugger/remix-debugger/test-browser/resources/testWeb3.json b/src/app/debugger/remix-debugger/test-browser/resources/testWeb3.json
new file mode 100644
index 0000000000..ba5f88cb0f
--- /dev/null
+++ b/src/app/debugger/remix-debugger/test-browser/resources/testWeb3.json
@@ -0,0 +1,17 @@
+{
+ "testTxs": {
+ "0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd51": {"blockHash":"0xd1d34932f8733e0485b7d9bf8500c4046d650f20ed7792508c304304fa7bbfac","blockNumber":89,"from":"0x00101c5bfa3fc8bad02c9f5fd65b069306251915","gas":105967,"gasPrice":"20000000000","hash":"0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd51","input":"0x60fe47b10000000000000000000000000000000000000000000000000000000000000038","nonce":3,"to":"0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5","transactionIndex":0,"value":"0"}
+ },
+
+ "testTxsByBlock": {
+ "105967-0": {"blockHash":"0xd1d34932f8733e0485b7d9bf8500c4046d650f20ed7792508c304304fa7bbfac","blockNumber":89,"from":"0x00101c5bfa3fc8bad02c9f5fd65b069306251915","gas":105967,"gasPrice":"20000000000","hash":"0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd51","input":"0x60fe47b10000000000000000000000000000000000000000000000000000000000000038","nonce":3,"to":"0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5","transactionIndex":0,"value":"0"}
+ },
+
+ "testCodes": {
+ "0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5": "0x60606040526000357c01000000000000000000000000000000000000000000000000000000009004806360fe47b11460415780636d4ce63c14605757603f565b005b605560048080359060200190919050506089565b005b606260048050506078565b6040518082815260200191505060405180910390f35b600060006000505490506086565b90565b80600060005081905550602d6040516045806100f083390180828152602001915050604051809103906000f0600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff02191690830217905550602281016000600050819055505b505660606040526040516020806045833981016040528080519060200190919050505b806001016000600050819055505b50600a80603b6000396000f360606040526008565b00"
+ },
+
+ "testTraces": {
+ "0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd51": {"gas":"0x0000000000000000000000000000000000000000000000000000000000019def","return":"0x","structLogs":[{"gas":"84503","gasCost":"3","memory":[],"op":"PUSH1","pc":"0","stack":[]},{"gas":"84500","gasCost":"3","op":"PUSH1","pc":"2","stack":["0x60"]},{"gas":"84497","gasCost":"12","memexpand":"3","op":"MSTORE","pc":"4","stack":["0x60","0x40"]},{"gas":"84485","gasCost":"3","memory":["0000000000000000000000000000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000060"],"op":"PUSH1","pc":"5","stack":[]},{"gas":"84482","gasCost":"3","op":"CALLDATALOAD","pc":"7","stack":["0x00"]},{"gas":"84479","gasCost":"3","op":"PUSH29","pc":"8","stack":["0x60fe47b100000000000000000000000000000000000000000000000000000000"]},{"gas":"84476","gasCost":"3","op":"SWAP1","pc":"38","stack":["0x60fe47b100000000000000000000000000000000000000000000000000000000","0x0100000000000000000000000000000000000000000000000000000000"]},{"gas":"84473","gasCost":"5","op":"DIV","pc":"39","stack":["0x0100000000000000000000000000000000000000000000000000000000","0x60fe47b100000000000000000000000000000000000000000000000000000000"]},{"gas":"84468","gasCost":"3","op":"DUP1","pc":"40","stack":["0x60fe47b1"]},{"gas":"84465","gasCost":"3","op":"PUSH4","pc":"41","stack":["0x60fe47b1","0x60fe47b1"]},{"gas":"84462","gasCost":"3","op":"EQ","pc":"46","stack":["0x60fe47b1","0x60fe47b1","0x60fe47b1"]},{"gas":"84459","gasCost":"3","op":"PUSH1","pc":"47","stack":["0x60fe47b1","0x01"]},{"gas":"84456","gasCost":"10","op":"JUMPI","pc":"49","stack":["0x60fe47b1","0x01","0x41"]},{"gas":"84446","gasCost":"1","op":"JUMPDEST","pc":"65","stack":["0x60fe47b1"]},{"gas":"84445","gasCost":"3","op":"PUSH1","pc":"66","stack":["0x60fe47b1"]},{"gas":"84442","gasCost":"3","op":"PUSH1","pc":"68","stack":["0x60fe47b1","0x55"]},{"gas":"84439","gasCost":"3","op":"DUP1","pc":"70","stack":["0x60fe47b1","0x55","0x04"]},{"gas":"84436","gasCost":"3","op":"DUP1","pc":"71","stack":["0x60fe47b1","0x55","0x04","0x04"]},{"gas":"84433","gasCost":"3","op":"CALLDATALOAD","pc":"72","stack":["0x60fe47b1","0x55","0x04","0x04","0x04"]},{"gas":"84430","gasCost":"3","op":"SWAP1","pc":"73","stack":["0x60fe47b1","0x55","0x04","0x04","0x38"]},{"gas":"84427","gasCost":"3","op":"PUSH1","pc":"74","stack":["0x60fe47b1","0x55","0x04","0x38","0x04"]},{"gas":"84424","gasCost":"3","op":"ADD","pc":"76","stack":["0x60fe47b1","0x55","0x04","0x38","0x04","0x20"]},{"gas":"84421","gasCost":"3","op":"SWAP1","pc":"77","stack":["0x60fe47b1","0x55","0x04","0x38","0x24"]},{"gas":"84418","gasCost":"3","op":"SWAP2","pc":"78","stack":["0x60fe47b1","0x55","0x04","0x24","0x38"]},{"gas":"84415","gasCost":"3","op":"SWAP1","pc":"79","stack":["0x60fe47b1","0x55","0x38","0x24","0x04"]},{"gas":"84412","gasCost":"2","op":"POP","pc":"80","stack":["0x60fe47b1","0x55","0x38","0x04","0x24"]},{"gas":"84410","gasCost":"2","op":"POP","pc":"81","stack":["0x60fe47b1","0x55","0x38","0x04"]},{"gas":"84408","gasCost":"3","op":"PUSH1","pc":"82","stack":["0x60fe47b1","0x55","0x38"]},{"gas":"84405","gasCost":"8","op":"JUMP","pc":"84","stack":["0x60fe47b1","0x55","0x38","0x89"]},{"gas":"84397","gasCost":"1","op":"JUMPDEST","pc":"137","stack":["0x60fe47b1","0x55","0x38"]},{"gas":"84396","gasCost":"3","op":"DUP1","pc":"138","stack":["0x60fe47b1","0x55","0x38"]},{"gas":"84393","gasCost":"3","op":"PUSH1","pc":"139","stack":["0x60fe47b1","0x55","0x38","0x38"]},{"gas":"84390","gasCost":"3","op":"PUSH1","pc":"141","stack":["0x60fe47b1","0x55","0x38","0x38","0x00"]},{"gas":"84387","gasCost":"2","op":"POP","pc":"143","stack":["0x60fe47b1","0x55","0x38","0x38","0x00","0x00"]},{"gas":"84385","gasCost":"3","op":"DUP2","pc":"144","stack":["0x60fe47b1","0x55","0x38","0x38","0x00"]},{"gas":"84382","gasCost":"3","op":"SWAP1","pc":"145","stack":["0x60fe47b1","0x55","0x38","0x38","0x00","0x38"]},{"gas":"84379","gasCost":"5000","op":"SSTORE","pc":"146","stack":["0x60fe47b1","0x55","0x38","0x38","0x38","0x00"]},{"gas":"79379","gasCost":"2","op":"POP","pc":"147","stack":["0x60fe47b1","0x55","0x38","0x38"]},{"gas":"79377","gasCost":"3","op":"PUSH1","pc":"148","stack":["0x60fe47b1","0x55","0x38"]},{"gas":"79374","gasCost":"3","op":"PUSH1","pc":"150","stack":["0x60fe47b1","0x55","0x38","0x2d"]},{"gas":"79371","gasCost":"3","op":"MLOAD","pc":"152","stack":["0x60fe47b1","0x55","0x38","0x2d","0x40"]},{"gas":"79368","gasCost":"3","memory":["0000000000000000000000000000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000060"],"op":"PUSH1","pc":"153","stack":["0x60fe47b1","0x55","0x38","0x2d","0x60"]},{"gas":"79365","gasCost":"3","op":"DUP1","pc":"155","stack":["0x60fe47b1","0x55","0x38","0x2d","0x60","0x45"]},{"gas":"79362","gasCost":"3","op":"PUSH2","pc":"156","stack":["0x60fe47b1","0x55","0x38","0x2d","0x60","0x45","0x45"]},{"gas":"79359","gasCost":"3","op":"DUP4","pc":"159","stack":["0x60fe47b1","0x55","0x38","0x2d","0x60","0x45","0x45","0xf0"]},{"gas":"79356","gasCost":"21","memexpand":"3","op":"CODECOPY","pc":"160","stack":["0x60fe47b1","0x55","0x38","0x2d","0x60","0x45","0x45","0xf0","0x60"]},{"gas":"79335","gasCost":"3","memory":["0000000000000000000000000000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000060","6060604052604051602080604583398101604052808051906020019091905050","5b806001016000600050819055505b50600a80603b6000396000f36060604052","6008565b00000000000000000000000000000000000000000000000000000000"],"op":"ADD","pc":"161","stack":["0x60fe47b1","0x55","0x38","0x2d","0x60","0x45"]},{"gas":"79332","gasCost":"3","op":"DUP1","pc":"162","stack":["0x60fe47b1","0x55","0x38","0x2d","0xa5"]},{"gas":"79329","gasCost":"3","op":"DUP3","pc":"163","stack":["0x60fe47b1","0x55","0x38","0x2d","0xa5","0xa5"]},{"gas":"79326","gasCost":"3","op":"DUP2","pc":"164","stack":["0x60fe47b1","0x55","0x38","0x2d","0xa5","0xa5","0x2d"]},{"gas":"79323","gasCost":"6","memexpand":"1","op":"MSTORE","pc":"165","stack":["0x60fe47b1","0x55","0x38","0x2d","0xa5","0xa5","0x2d","0xa5"]},{"gas":"79317","gasCost":"3","memory":["0000000000000000000000000000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000060","6060604052604051602080604583398101604052808051906020019091905050","5b806001016000600050819055505b50600a80603b6000396000f36060604052","6008565b00000000000000000000000000000000000000000000000000000000","000000002d000000000000000000000000000000000000000000000000000000"],"op":"PUSH1","pc":"166","stack":["0x60fe47b1","0x55","0x38","0x2d","0xa5","0xa5"]},{"gas":"79314","gasCost":"3","op":"ADD","pc":"168","stack":["0x60fe47b1","0x55","0x38","0x2d","0xa5","0xa5","0x20"]},{"gas":"79311","gasCost":"3","op":"SWAP2","pc":"169","stack":["0x60fe47b1","0x55","0x38","0x2d","0xa5","0xc5"]},{"gas":"79308","gasCost":"2","op":"POP","pc":"170","stack":["0x60fe47b1","0x55","0x38","0xc5","0xa5","0x2d"]},{"gas":"79306","gasCost":"2","op":"POP","pc":"171","stack":["0x60fe47b1","0x55","0x38","0xc5","0xa5"]},{"gas":"79304","gasCost":"3","op":"PUSH1","pc":"172","stack":["0x60fe47b1","0x55","0x38","0xc5"]},{"gas":"79301","gasCost":"3","op":"MLOAD","pc":"174","stack":["0x60fe47b1","0x55","0x38","0xc5","0x40"]},{"gas":"79298","gasCost":"3","memory":["0000000000000000000000000000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000060","6060604052604051602080604583398101604052808051906020019091905050","5b806001016000600050819055505b50600a80603b6000396000f36060604052","6008565b00000000000000000000000000000000000000000000000000000000","000000002d000000000000000000000000000000000000000000000000000000"],"op":"DUP1","pc":"175","stack":["0x60fe47b1","0x55","0x38","0xc5","0x60"]},{"gas":"79295","gasCost":"3","op":"SWAP2","pc":"176","stack":["0x60fe47b1","0x55","0x38","0xc5","0x60","0x60"]},{"gas":"79292","gasCost":"3","op":"SUB","pc":"177","stack":["0x60fe47b1","0x55","0x38","0x60","0x60","0xc5"]},{"gas":"79289","gasCost":"3","op":"SWAP1","pc":"178","stack":["0x60fe47b1","0x55","0x38","0x60","0x65"]},{"gas":"79286","gasCost":"3","op":"PUSH1","pc":"179","stack":["0x60fe47b1","0x55","0x38","0x65","0x60"]},{"gas":"79283","gasCost":"32000","op":"CREATE","pc":"181","stack":["0x60fe47b1","0x55","0x38","0x65","0x60","0x00"]},{"gas":"47283","gasCost":"3","memory":[],"op":"PUSH1","pc":"0","stack":[]},{"gas":"47280","gasCost":"3","op":"PUSH1","pc":"2","stack":["0x60"]},{"gas":"47277","gasCost":"12","memexpand":"3","op":"MSTORE","pc":"4","stack":["0x60","0x40"]},{"gas":"47265","gasCost":"3","memory":["0000000000000000000000000000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000060"],"op":"PUSH1","pc":"5","stack":[]},{"gas":"47262","gasCost":"3","op":"MLOAD","pc":"7","stack":["0x40"]},{"gas":"47259","gasCost":"3","memory":["0000000000000000000000000000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000060"],"op":"PUSH1","pc":"8","stack":["0x60"]},{"gas":"47256","gasCost":"3","op":"DUP1","pc":"10","stack":["0x60","0x20"]},{"gas":"47253","gasCost":"3","op":"PUSH1","pc":"11","stack":["0x60","0x20","0x20"]},{"gas":"47250","gasCost":"3","op":"DUP4","pc":"13","stack":["0x60","0x20","0x20","0x45"]},{"gas":"47247","gasCost":"9","memexpand":"1","op":"CODECOPY","pc":"14","stack":["0x60","0x20","0x20","0x45","0x60"]},{"gas":"47238","gasCost":"3","memory":["0000000000000000000000000000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000060","000000000000000000000000000000000000000000000000000000000000002d"],"op":"DUP2","pc":"15","stack":["0x60","0x20"]},{"gas":"47235","gasCost":"3","op":"ADD","pc":"16","stack":["0x60","0x20","0x60"]},{"gas":"47232","gasCost":"3","op":"PUSH1","pc":"17","stack":["0x60","0x80"]},{"gas":"47229","gasCost":"3","op":"MSTORE","pc":"19","stack":["0x60","0x80","0x40"]},{"gas":"47226","gasCost":"3","memory":["0000000000000000000000000000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000080","000000000000000000000000000000000000000000000000000000000000002d"],"op":"DUP1","pc":"20","stack":["0x60"]},{"gas":"47223","gasCost":"3","op":"DUP1","pc":"21","stack":["0x60","0x60"]},{"gas":"47220","gasCost":"3","op":"MLOAD","pc":"22","stack":["0x60","0x60","0x60"]},{"gas":"47217","gasCost":"3","memory":["0000000000000000000000000000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000080","000000000000000000000000000000000000000000000000000000000000002d"],"op":"SWAP1","pc":"23","stack":["0x60","0x60","0x2d"]},{"gas":"47214","gasCost":"3","op":"PUSH1","pc":"24","stack":["0x60","0x2d","0x60"]},{"gas":"47211","gasCost":"3","op":"ADD","pc":"26","stack":["0x60","0x2d","0x60","0x20"]},{"gas":"47208","gasCost":"3","op":"SWAP1","pc":"27","stack":["0x60","0x2d","0x80"]},{"gas":"47205","gasCost":"3","op":"SWAP2","pc":"28","stack":["0x60","0x80","0x2d"]},{"gas":"47202","gasCost":"3","op":"SWAP1","pc":"29","stack":["0x2d","0x80","0x60"]},{"gas":"47199","gasCost":"2","op":"POP","pc":"30","stack":["0x2d","0x60","0x80"]},{"gas":"47197","gasCost":"2","op":"POP","pc":"31","stack":["0x2d","0x60"]},{"gas":"47195","gasCost":"1","op":"JUMPDEST","pc":"32","stack":["0x2d"]},{"gas":"47194","gasCost":"3","op":"DUP1","pc":"33","stack":["0x2d"]},{"gas":"47191","gasCost":"3","op":"PUSH1","pc":"34","stack":["0x2d","0x2d"]},{"gas":"47188","gasCost":"3","op":"ADD","pc":"36","stack":["0x2d","0x2d","0x01"]},{"gas":"47185","gasCost":"3","op":"PUSH1","pc":"37","stack":["0x2d","0x2e"]},{"gas":"47182","gasCost":"3","op":"PUSH1","pc":"39","stack":["0x2d","0x2e","0x00"]},{"gas":"47179","gasCost":"2","op":"POP","pc":"41","stack":["0x2d","0x2e","0x00","0x00"]},{"gas":"47177","gasCost":"3","op":"DUP2","pc":"42","stack":["0x2d","0x2e","0x00"]},{"gas":"47174","gasCost":"3","op":"SWAP1","pc":"43","stack":["0x2d","0x2e","0x00","0x2e"]},{"gas":"47171","gasCost":"20000","op":"SSTORE","pc":"44","stack":["0x2d","0x2e","0x2e","0x00"]},{"gas":"27171","gasCost":"2","op":"POP","pc":"45","stack":["0x2d","0x2e"]},{"gas":"27169","gasCost":"1","op":"JUMPDEST","pc":"46","stack":["0x2d"]},{"gas":"27168","gasCost":"2","op":"POP","pc":"47","stack":["0x2d"]},{"gas":"27166","gasCost":"3","op":"PUSH1","pc":"48","stack":[]},{"gas":"27163","gasCost":"3","op":"DUP1","pc":"50","stack":["0x0a"]},{"gas":"27160","gasCost":"3","op":"PUSH1","pc":"51","stack":["0x0a","0x0a"]},{"gas":"27157","gasCost":"3","op":"PUSH1","pc":"53","stack":["0x0a","0x0a","0x3b"]},{"gas":"27154","gasCost":"6","op":"CODECOPY","pc":"55","stack":["0x0a","0x0a","0x3b","0x00"]},{"gas":"27148","gasCost":"3","memory":["60606040526008565b0000000000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000080","000000000000000000000000000000000000000000000000000000000000002d"],"op":"PUSH1","pc":"56","stack":["0x0a"]},{"gas":"27145","gasCost":"0","op":"RETURN","pc":"58","stack":["0x0a","0x00"]},{"gas":"25145","gasCost":"3","memory":["0000000000000000000000000000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000060","6060604052604051602080604583398101604052808051906020019091905050","5b806001016000600050819055505b50600a80603b6000396000f36060604052","6008565b00000000000000000000000000000000000000000000000000000000","000000002d000000000000000000000000000000000000000000000000000000"],"op":"PUSH1","pc":"182","stack":["0x60fe47b1","0x55","0x38","0xd01f65e3472f24faf45f08f8698ec4da1bf32a95"]},{"gas":"25142","gasCost":"3","memory":["0000000000000000000000000000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000060","6060604052604051602080604583398101604052808051906020019091905050","5b806001016000600050819055505b50600a80603b6000396000f36060604052","6008565b00000000000000000000000000000000000000000000000000000000","000000002d000000000000000000000000000000000000000000000000000000"],"op":"PUSH1","pc":"184","stack":["0x60fe47b1","0x55","0x38","0xd01f65e3472f24faf45f08f8698ec4da1bf32a95","0x01"]},{"gas":"25139","gasCost":"3","op":"PUSH2","pc":"186","stack":["0x60fe47b1","0x55","0x38","0xd01f65e3472f24faf45f08f8698ec4da1bf32a95","0x01","0x00"]},{"gas":"25136","gasCost":"10","op":"EXP","pc":"189","stack":["0x60fe47b1","0x55","0x38","0xd01f65e3472f24faf45f08f8698ec4da1bf32a95","0x01","0x00","0x0100"]},{"gas":"25126","gasCost":"3","op":"DUP2","pc":"190","stack":["0x60fe47b1","0x55","0x38","0xd01f65e3472f24faf45f08f8698ec4da1bf32a95","0x01","0x01"]},{"gas":"25123","gasCost":"50","op":"SLOAD","pc":"191","stack":["0x60fe47b1","0x55","0x38","0xd01f65e3472f24faf45f08f8698ec4da1bf32a95","0x01","0x01","0x01"]},{"gas":"25073","gasCost":"3","op":"DUP2","pc":"192","stack":["0x60fe47b1","0x55","0x38","0xd01f65e3472f24faf45f08f8698ec4da1bf32a95","0x01","0x01","0x00"]},{"gas":"25070","gasCost":"3","op":"PUSH20","pc":"193","stack":["0x60fe47b1","0x55","0x38","0xd01f65e3472f24faf45f08f8698ec4da1bf32a95","0x01","0x01","0x00","0x01"]},{"gas":"25067","gasCost":"5","op":"MUL","pc":"214","stack":["0x60fe47b1","0x55","0x38","0xd01f65e3472f24faf45f08f8698ec4da1bf32a95","0x01","0x01","0x00","0x01","0xffffffffffffffffffffffffffffffffffffffff"]},{"gas":"25062","gasCost":"3","op":"NOT","pc":"215","stack":["0x60fe47b1","0x55","0x38","0xd01f65e3472f24faf45f08f8698ec4da1bf32a95","0x01","0x01","0x00","0xffffffffffffffffffffffffffffffffffffffff"]},{"gas":"25059","gasCost":"3","op":"AND","pc":"216","stack":["0x60fe47b1","0x55","0x38","0xd01f65e3472f24faf45f08f8698ec4da1bf32a95","0x01","0x01","0x00","0xffffffffffffffffffffffff0000000000000000000000000000000000000000"]},{"gas":"25056","gasCost":"3","op":"SWAP1","pc":"217","stack":["0x60fe47b1","0x55","0x38","0xd01f65e3472f24faf45f08f8698ec4da1bf32a95","0x01","0x01","0x00"]},{"gas":"25053","gasCost":"3","op":"DUP4","pc":"218","stack":["0x60fe47b1","0x55","0x38","0xd01f65e3472f24faf45f08f8698ec4da1bf32a95","0x01","0x00","0x01"]},{"gas":"25050","gasCost":"5","op":"MUL","pc":"219","stack":["0x60fe47b1","0x55","0x38","0xd01f65e3472f24faf45f08f8698ec4da1bf32a95","0x01","0x00","0x01","0xd01f65e3472f24faf45f08f8698ec4da1bf32a95"]},{"gas":"25045","gasCost":"3","op":"OR","pc":"220","stack":["0x60fe47b1","0x55","0x38","0xd01f65e3472f24faf45f08f8698ec4da1bf32a95","0x01","0x00","0xd01f65e3472f24faf45f08f8698ec4da1bf32a95"]},{"gas":"25042","gasCost":"3","op":"SWAP1","pc":"221","stack":["0x60fe47b1","0x55","0x38","0xd01f65e3472f24faf45f08f8698ec4da1bf32a95","0x01","0xd01f65e3472f24faf45f08f8698ec4da1bf32a95"]},{"gas":"25039","gasCost":"20000","op":"SSTORE","pc":"222","stack":["0x60fe47b1","0x55","0x38","0xd01f65e3472f24faf45f08f8698ec4da1bf32a95","0xd01f65e3472f24faf45f08f8698ec4da1bf32a95","0x01"]},{"gas":"5039","gasCost":"2","op":"POP","pc":"223","stack":["0x60fe47b1","0x55","0x38","0xd01f65e3472f24faf45f08f8698ec4da1bf32a95"]},{"gas":"5037","gasCost":"3","op":"PUSH1","pc":"224","stack":["0x60fe47b1","0x55","0x38"]},{"gas":"5034","gasCost":"3","op":"DUP2","pc":"226","stack":["0x60fe47b1","0x55","0x38","0x22"]},{"gas":"5031","gasCost":"3","op":"ADD","pc":"227","stack":["0x60fe47b1","0x55","0x38","0x22","0x38"]},{"gas":"5028","gasCost":"3","op":"PUSH1","pc":"228","stack":["0x60fe47b1","0x55","0x38","0x5a"]},{"gas":"5025","gasCost":"3","op":"PUSH1","pc":"230","stack":["0x60fe47b1","0x55","0x38","0x5a","0x00"]},{"gas":"5022","gasCost":"2","op":"POP","pc":"232","stack":["0x60fe47b1","0x55","0x38","0x5a","0x00","0x00"]},{"gas":"5020","gasCost":"3","op":"DUP2","pc":"233","stack":["0x60fe47b1","0x55","0x38","0x5a","0x00"]},{"gas":"5017","gasCost":"3","op":"SWAP1","pc":"234","stack":["0x60fe47b1","0x55","0x38","0x5a","0x00","0x5a"]},{"gas":"5014","gasCost":"5000","op":"SSTORE","pc":"235","stack":["0x60fe47b1","0x55","0x38","0x5a","0x5a","0x00"]},{"gas":"14","gasCost":"2","op":"POP","pc":"236","stack":["0x60fe47b1","0x55","0x38","0x5a"]},{"gas":"12","gasCost":"1","op":"JUMPDEST","pc":"237","stack":["0x60fe47b1","0x55","0x38"]},{"gas":"11","gasCost":"2","op":"POP","pc":"238","stack":["0x60fe47b1","0x55","0x38"]},{"gas":"9","gasCost":"8","op":"JUMP","pc":"239","stack":["0x60fe47b1","0x55"]},{"gas":"1","gasCost":"1","op":"JUMPDEST","pc":"85","stack":["0x60fe47b1"]},{"gas":"0","gasCost":"0","op":"STOP","pc":"86","stack":["0x60fe47b1"]}]}
+ }
+}
diff --git a/src/app/debugger/remix-debugger/test-browser/test/init.js b/src/app/debugger/remix-debugger/test-browser/test/init.js
new file mode 100644
index 0000000000..4078b00185
--- /dev/null
+++ b/src/app/debugger/remix-debugger/test-browser/test/init.js
@@ -0,0 +1,172 @@
+module.exports = function (browser, callback) {
+ extendBrowser(browser)
+ browser
+ .url('http://127.0.0.1:8080')
+ .injectScript('test-browser/resources/insertTestWeb3.js', function () {
+ // wait for the script to load test web3...
+ setTimeout(function () {
+ callback()
+ }, 5000)
+ })
+}
+
+function extendBrowser (browser) {
+ browser.multipleClick = function (id, time) {
+ for (var k = 0; k < time; k++) {
+ browser.click(id)
+ }
+ return browser
+ }
+
+ browser.assertCurrentSelectedItem = function (expected) {
+ browser.execute(function (id) {
+ var node = document.querySelector('#asmcodes div div[selected="selected"] span')
+ return node.innerText
+ }, [''], function (returnValue) {
+ browser.assert.equal(returnValue.value, expected)
+ })
+ return browser
+ }
+
+ browser.retrieveInnerText = function (selector, callback) {
+ browser.execute(function (selector) {
+ var node = document.querySelector(selector)
+ return node ? node.innerText : ''
+ }, [selector], function (returnValue) {
+ callback(returnValue.value)
+ })
+ return browser
+ }
+
+ browser.assertStepDetail = function (vmtracestepinfo, stepinfo, addmemoryinfo, gasinfo, remaininggasinfo, loadedaddressinfo) {
+ assertPanel('#stepdetail', browser, ['vmtracestep:' + vmtracestepinfo, 'executionstep:' + stepinfo, 'addmemory:' + addmemoryinfo, 'gas:' + gasinfo, 'remaininggas:' + remaininggasinfo, 'loadedaddress:' + loadedaddressinfo])
+ return browser
+ }
+
+ browser.assertStack = function (value) {
+ return assertPanel('#stackpanel', browser, value)
+ }
+
+ browser.assertStorageChanges = function (value) {
+ return assertPanel('#storagepanel', browser, value)
+ }
+
+ browser.assertMemory = function (value) {
+ return assertPanel('#memorypanel', browser, value)
+ }
+
+ browser.assertCallData = function (value) {
+ return assertPanel('#calldatapanel', browser, value)
+ }
+
+ browser.assertCallStack = function (value) {
+ return assertPanel('#callstackpanel', browser, value)
+ }
+
+ browser.assertStackValue = function (index, value) {
+ return assertPanelValue('#stackpanel', browser, index, value)
+ }
+
+ browser.assertStorageChangesValue = function (index, value) {
+ return assertPanelValue('#storagepanel', browser, index, value)
+ }
+
+ browser.assertMemoryValue = function (index, value) {
+ return assertPanelValue('#memorypanel', browser, index, value)
+ }
+
+ browser.assertCallStackValue = function (index, value) {
+ return assertPanelValue('#callstackpanel', browser, index, value)
+ }
+
+ browser.debugerKeyCode = {
+ 'Enter': 13,
+ 'Up': 38,
+ 'Down': 40,
+ 'Right': '39',
+ 'Left': 37,
+ 'Esc': 27,
+ 'SpaceBar': 32,
+ 'Ctrl': 17,
+ 'Alt': 18,
+ 'Shift': 16
+ }
+
+/* browser.sendKeys is not working for safari */
+/* still not working properly
+browser.fireEvent = function (el, key, times, callback) {
+ var data = {
+ 'id': el.substring(1),
+ 'key': key,
+ 'times': times
+ }
+ browser.execute(function (data) {
+ data = JSON.parse(data)
+ var el = document.getElementById(data.id)
+ var eventObj
+ console.log(el)
+ console.log(data)
+ var k = 0
+ if (document.createEventObject) {
+ eventObj = document.createEventObject()
+ eventObj.keyCode = data.key
+ while (k < data.times) {
+ console.log('firing brfore createEventObject')
+ el.fireEvent('onkeypress', eventObj)
+ console.log('firing')
+ k++
+ }
+ } else if (typeof (KeyboardEvent) === 'function') {
+ eventObj = new KeyboardEvent('keyup')
+ eventObj.key = data.key
+ eventObj.which = data.key
+ while (k < data.times) {
+ console.log('firing brfore createEvent')
+ el.dispatchEvent(eventObj)
+ console.log('firing')
+ k++
+ }
+ }
+ }, [JSON.stringify(data)], function () {
+ callback()
+ })
+}
+*/
+}
+
+function assertPanel (id, browser, value) {
+ var selector = '.dropdownpanel div.dropdowncontent ul'
+ browser.execute(function (id, selector) {
+ var el = document.getElementById(id.replace('#', '').replace('.', ''))
+ var node = el.querySelector(selector)
+ var ret = []
+ for (var k = 0; k < node.children.length; k++) {
+ if (node.children[k].innerText) {
+ ret.push(node.children[k].innerText)
+ }
+ }
+ return ret
+ }, [id, selector], function (returnValues) {
+ value.map(function (item, index) {
+ if (returnValues.value.length) {
+ var testValue = returnValues.value[index].replace(/\r\n/g, '').replace(/\t/g, '').replace(/\s/g, '')
+ browser.assert.equal(testValue, value[index])
+ } else {
+ browser.assert.equal(item, '')
+ }
+ })
+ })
+ return browser
+}
+
+function assertPanelValue (id, browser, index, value) {
+ var selector = id + ' .dropdownpanel .dropdowncontent ul'
+ browser.execute(function (id, index) {
+ var node = document.querySelector(id)
+ return node.children[index].innerText
+ }, [selector, index], function (returnValues) {
+ var testValue = returnValues.value.replace(/\r\n/g, '').replace(/\t/g, '').replace(/\s/g, '')
+ browser.assert.equal(testValue, value)
+ })
+ return browser
+}
diff --git a/src/app/debugger/remix-debugger/test-browser/test/sauce.js b/src/app/debugger/remix-debugger/test-browser/test/sauce.js
new file mode 100644
index 0000000000..4a41fe5930
--- /dev/null
+++ b/src/app/debugger/remix-debugger/test-browser/test/sauce.js
@@ -0,0 +1,61 @@
+const https = require('https')
+
+module.exports = function sauce (callback) {
+ const currentTest = this.client.currentTest
+ const username = this.client.options.username
+ const sessionId = this.client.capabilities['webdriver.remote.sessionid']
+ const accessKey = this.client.options.accessKey
+
+ if (!this.client.launch_url.match(/saucelabs/)) {
+ console.log('Not saucelabs ...')
+ return callback()
+ }
+
+ if (!username || !accessKey || !sessionId) {
+ console.log(this.client)
+ console.log('No username, accessKey or sessionId')
+ return callback()
+ }
+
+ const passed = currentTest.results.passed === currentTest.results.tests
+
+ const data = JSON.stringify({passed})
+
+ const requestPath = `/rest/v1/${username}/jobs/${sessionId}`
+
+ function responseCallback (res) {
+ res.setEncoding('utf8')
+ console.log('Response: ', res.statusCode, JSON.stringify(res.headers))
+ res.on('data', function onData (chunk) {
+ console.log('BODY: ' + chunk)
+ })
+ res.on('end', function onEnd () {
+ console.info('Finished updating saucelabs')
+ callback()
+ })
+ }
+
+ try {
+ console.log('Updating saucelabs', requestPath)
+
+ const req = https.request({
+ hostname: 'saucelabs.com',
+ path: requestPath,
+ method: 'PUT',
+ auth: `${username}:${accessKey}`,
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Content-Length': data.length
+ }
+ }, responseCallback)
+
+ req.on('error', function onError (e) {
+ console.log('problem with request: ' + e.message)
+ })
+ req.write(data)
+ req.end()
+ } catch (error) {
+ console.log('Error', error)
+ callback()
+ }
+}
diff --git a/src/app/debugger/remix-debugger/test-browser/test/vmdebugger.js b/src/app/debugger/remix-debugger/test-browser/test/vmdebugger.js
new file mode 100644
index 0000000000..f84555a0f3
--- /dev/null
+++ b/src/app/debugger/remix-debugger/test-browser/test/vmdebugger.js
@@ -0,0 +1,192 @@
+'use strict'
+var init = require('./init')
+var sauce = require('./sauce')
+
+module.exports = {
+ beforeEach: function (browser, done) {
+ try {
+ init(browser, function () {
+ done()
+ })
+ } catch (e) {
+ var mes = 'error ' + e.message
+ console.log(mes)
+ done(mes)
+ }
+ },
+
+ 'vmdebugger': function (browser) {
+ loadTraceNotFound(browser)
+ .click('#unload')
+ loadTrace(browser)
+ .click('#unload')
+ panels(browser)
+ .click('#unload')
+ slider(browser)
+ .click('#unload')
+ stepping(browser)
+ .click('#unload')
+ stepdetail(browser)
+ .end()
+ },
+
+ tearDown: sauce
+}
+
+function loadTraceNotFound (browser) {
+ browser
+ .clearValue('#txinput')
+ .setValue('#txinput', '0x20ef65b8b186ca942zcccd634f37074dde49b541c27994fc7596740ef44cfd51')
+ .click('#load')
+ .click('#txinfo .title')
+ .execute(function () {
+ return document.querySelector('#txinfo .dropdownpanel .dropdownrawcontent').innerHTML
+ }, [], function (result) {
+ console.log(result.value)
+ if (result.value.indexOf('not found') === -1) {
+ browser.assert.fail(' txinput panel does not contain ', 'info about error', '')
+ }
+ })
+ return browser
+}
+
+function loadTrace (browser) {
+ browser
+ .clearValue('#txinput')
+ .setValue('#txinput', '0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd51')
+ .click('#load')
+ .click('#txinfo .title')
+ .execute(function () {
+ return document.querySelector('#txinfo .dropdownpanel .dropdownrawcontent').innerHTML
+ }, [], function (result) {
+ if (result.value.indexOf('0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd51') === -1) {
+ browser.assert.fail(' txinput panel does not contain 0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd51 ', 'info about error', '')
+ }
+ })
+ .click('#unload')
+ .waitForElementNotVisible('#vmdebugger', 1000)
+ return browser
+}
+
+function panels (browser) {
+ browser
+ .clearValue('#txinput')
+ .setValue('#txinput', '0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd51')
+ .click('#load')
+ .multipleClick('#intoforward', 63)
+ .assertStack(['0:0x', '1:0x60', '2:0x65', '3:0x38', '4:0x55', '5:0x60fe47b1'])
+ .assertStorageChanges(['0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563:Objectkey:0x00value:0x38'])
+ .assertCallData(['0:0x60fe47b10000000000000000000000000000000000000000000000000000000000000038'])
+ .assertCallStack(['0:0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5'])
+ .assertStackValue(1, '1:0x60')
+ .assertMemoryValue(6, '0x60:60606040526040516020806045833981????R??Q????E?9?')
+ .assertMemoryValue(7, '0x70:01604052808051906020019091905050???R??Q???????PP')
+ .assertMemoryValue(8, '0x80:5b806001016000600050819055505b50?????????P??UP?P')
+ .click('#intoforward') // CREATE
+ .assertStack([''])
+ .assertStorageChanges([])
+ .assertMemory([''])
+ .assertCallData(['0:0x0000000000000000000000000000000000000000000000000000000000000000000000000000006060606040526040516020806045833981016040528080519060200190919050505b806001016000600050819055'])
+ .assertCallStack(['0:0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5', '1:(ContractCreation-Step63)'])
+ return browser
+}
+
+function slider (browser) {
+ browser
+ .clearValue('#txinput')
+ .setValue('#txinput', '0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd51')
+ .click('#load')
+ .click('#intoforward')
+ .click('#intoforward')
+ .click('#intoforward')
+ .click('#intoforward')
+ .click('#intoforward')
+ .click('#intoforward')
+ .click('#intoforward')
+ .click('#intoforward')
+ .click('#intoforward')
+ /*
+ .sendKeys('#slider', browser.Keys.RIGHT_ARROW)
+ .sendKeys('#slider', browser.Keys.RIGHT_ARROW)
+ .sendKeys('#slider', browser.Keys.RIGHT_ARROW)
+ .sendKeys('#slider', browser.Keys.RIGHT_ARROW)
+ .sendKeys('#slider', browser.Keys.RIGHT_ARROW)
+ .sendKeys('#slider', browser.Keys.RIGHT_ARROW)
+ .sendKeys('#slider', browser.Keys.RIGHT_ARROW)
+ .sendKeys('#slider', browser.Keys.RIGHT_ARROW)
+ .sendKeys('#slider', browser.Keys.LEFT_ARROW)
+ */
+ .assertCurrentSelectedItem('041 PUSH4 60fe47b1')
+ return browser
+}
+
+function stepping (browser) {
+ browser
+ .clearValue('#txinput')
+ .setValue('#txinput', '0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd51')
+ .click('#load')
+ .click('#intoforward')
+ .click('#intoforward')
+ .assertCurrentSelectedItem('004 MSTORE')
+ .click('#intoforward')
+ .click('#intoback')
+ .click('#intoback')
+ .assertCurrentSelectedItem('002 PUSH1 40')
+ .multipleClick('#intoforward', 62)
+ .assertCurrentSelectedItem('181 CREATE')
+ .click('#intoforward')
+ .click('#intoforward')
+ .click('#intoforward')
+ .click('#intoforward')
+ .click('#overforward')
+ .assertCurrentSelectedItem('007 MLOAD')
+ .click('#intoback')
+ .click('#intoback')
+ .click('#intoback')
+ .click('#intoback')
+ .click('#intoback')
+ .click('#overforward')
+ .assertCurrentSelectedItem('182 PUSH1 01')
+ .click('#overforward')
+ .assertCurrentSelectedItem('184 PUSH1 00')
+ .click('#intoback')
+ .click('#intoback')
+ .click('#overback')
+ .assertCurrentSelectedItem('181 CREATE')
+ return browser
+}
+
+function stepdetail (browser) {
+ browser
+ .clearValue('#txinput')
+ .setValue('#txinput', '0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd51')
+ .click('#load')
+ .click('#intoforward')
+ .click('#intoforward')
+ .click('#intoforward')
+ .click('#intoforward')
+ .click('#intoforward')
+ .click('#intoforward')
+ /*
+ .fireEvent('#slider', browser.debugerKeyCode.Right, 4, function () {
+ browser.assertSticker('6', '6', '', '3', '84476', '0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5')
+ .click('#nextcall')
+ .assertSticker('63', '63', '', '32000', '79283', '0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5')
+ .click('#intoforward')
+ .click('#overforward')
+ .assertSticker('108', '44', '', '0', '27145', '(Contract Creation - Step 63)')
+ .click('#intoforward')
+ .assertSticker('109', '64', '', '3', '25145', '0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5')
+ .end()
+ })
+ */
+ .assertStepDetail('6', '6', '', '3', '84476', '0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5')
+ .multipleClick('#intoforward', 57)
+ .assertStepDetail('63', '63', '', '32000', '79283', '0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5')
+ .click('#overforward')
+ .click('#intoback')
+ .assertStepDetail('108', '44', '', '0', '27145', '(ContractCreation-Step63)')
+ .click('#intoforward')
+ .assertStepDetail('109', '64', '', '3', '25145', '0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5')
+ return browser
+}