diff --git a/.circleci/config.yml b/.circleci/config.yml
new file mode 100644
index 0000000000..0b07bbd8b7
--- /dev/null
+++ b/.circleci/config.yml
@@ -0,0 +1,44 @@
+# Javascript Node CircleCI 2.0 configuration file
+#
+# Check https://circleci.com/docs/2.0/language-javascript/ for more details
+#
+version: 2
+jobs:
+ remix-lib:
+ docker:
+ - image: circleci/node:7.10
+ environment:
+ working_directory: ~/repo
+ steps:
+ - checkout
+ - run: npm install && npm run bootstrap
+ - run: cd remix-lib && npm test
+
+ remix-debug:
+ docker:
+ - image: circleci/node:7.10
+ environment:
+ working_directory: ~/repo
+ steps:
+ - checkout
+ - run: npm install && npm run bootstrap
+ - run: cd remix-debug && npm test
+
+ remix-analyzer:
+ docker:
+ - image: circleci/node:7.10
+ environment:
+ working_directory: ~/repo
+ steps:
+ - checkout
+ - run: npm install && npm run bootstrap
+ - run: cd remix-analyzer && npm test
+
+workflows:
+ version: 2
+ build_all:
+ jobs:
+ - remix-lib
+ - remix-debug
+ - remix-analyzer
+
diff --git a/.gitignore b/.gitignore
index 920bf3aade..68f8dbe9a6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,8 @@ npm-debug.log
lint.xml
.vscode
test-browser/reports/*
+babelify-src
+docs/_build
+package-lock.json
+.DS_Store
+.tern-port
diff --git a/.travis.yml b/.travis.yml
index eb6e4e4d37..5fd298c5b5 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,13 +1,18 @@
language: node_js
node_js:
- stable
+env:
+ - TEST_DIR=remix-lib
+ - TEST_DIR=remix-solidity
+ - TEST_DIR=remix-debug
script:
- - npm run test
- - ./ci/browser_tests.sh
+ - cd $TEST_DIR && npm install && npm test
deploy:
provider: script
- script: ci/deploy_from_travis.sh
+ script: remix-debugger/ci/deploy_from_travis.sh
skip_cleanup: true
on:
branch: master
+ condition: $TEST_DIR = remix-debugger
+cache: false
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000000..ae1309a1dd
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,22 @@
+# Contributing
+
+Everyone is very welcome to contribute on the codebase of Remix. Please reach us in [Gitter](https://gitter.im/ethereum/remix)!
+
+The easiest way to work on `remix-debugger` or `remix-solidity` is to pull the `remix-ide` (Remix IDE) repository (https://github.com/ethereum/remix-ide) which has a strong Remix integration. Then, in `remix-ide`, execute `npm install`, `npm run pullremix`, `npm run linkremixsolidity`.
+then `npm run build && npm run serve` to start a new Remix IDE instance (you can browse `127.0.0.1:8080`).
+
+To interact with the Remix code:
+
+1. For static analysis: compile a new contact, click on the `Analysis`, the content of this tab is provided by the `remix-solidity/analysis` library
+
+2. For Debugging: compile a new contact, deploy it (`Create` button in the `run` tab), in the remix IDE terminal a transaction should appear, click on `Debug`. The Debugger tab get the focus, the content if this tab is provided by the `remix-debugger` library.
+
+3. For Decoding local and state variables: follow 2), then expand (is not already) the `Solidity State` and `Solidity Locals` panels, the content of thoses panel are provided by the `remix-solidity/decoder` library.
+
+**Reminder**: the Remix repository contains tools used to debug transactions, one of these tools being a debugger. The [`remix-ide` repository](https://github.com/ethereum/remix-ide) contains the Remix IDE (online version remix.ethereum.org), which make use of the Remix repository for the debugging features.
+
+## Coding style
+
+Remix makes use of the `npm` [coding style](https://docs.npmjs.com/misc/coding-style). Please make sure your code is compliant with this coding standard before sending a PR. You'll find in the link above tools to integrate this coding style with multiple developer tools (Emacs, Atom...). You can also make use of the `npm run test` which will check your local repository against the coding style.
+
+
diff --git a/README.md b/README.md
index 1007029706..88bff7cbc2 100644
--- a/README.md
+++ b/README.md
@@ -1,84 +1,52 @@
-# REMIX
+# Remix
[](https://gitter.im/ethereum/remix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
-Ethereum IDE and tools for the web
-## REMIX WEBSITE:
+Ethereum tools for the web.
-Remix is avalaible at http://ethereum.github.io/remix.
-You can use it either inside Mist or by connecting to geth or eth.
-Note that connecting to Geth does not work through https.
+*Are you looking for the Remix IDE? Follow [this link](https://github.com/ethereum/remix-ide)!*
-You'll have to run your own node using the following parameters:
++ [What is Remix?](#what-is-remix)
++ [How to use Remix?](#how-to-use)
++ [Modules](#modules)
++ [Contributing guidelines](#contributing)
-Using Geth:
+## What is Remix?
- geth --rpc --rpcapi 'web3,eth,debug' --rpcport 8545 --rpccorsdomain '*'
-
-Using Eth:
+**Remix** is a suite of tools to interact with the Ethereum blockchain in order to debug transactions, stored in this Git repository. A Remix transaction Web debugger is available [here](http://remix.ethereum.org), and its source code is part of this repository.
- eth -j --rpccorsdomain '*'
+The **Remix IDE** is an IDE for Solidity dApp developers, powered by Remix. The Remix IDE repository **is available [here](https://github.com/ethereum/remix-ide)**, and an online version is available at https://remix.ethereum.org.
-geth will run the rpc server on http://localhost:8545, remix uses by default this url.
+## How to use Remix
-Remix will use this instance of Geth to retrieve the transaction and the associated trace.
-This instance should **only** be used for debugging purposes. Never use features that could have an impact on your keys (do not unlock any keys, do not use this instance together with the wallet, do not activate the admin web3 API).
+### Prerequisites
-## INSTALLATION:
+To use Remix tools, you'll need to connect to an Ethereum node. You can do that using [the Mist browser](https://github.com/ethereum/mist), or by connecting to your local Ethereum node (`geth`, or `eth`). *Note: connecting to `geth` does not work through https.*
-Brief instructions to build for linux(Todo add other platforms) we will add detailed instructions later
++ Using `geth`: `geth --rpc --rpcapi 'web3,eth,debug' --rpcport 8545 --rpccorsdomain '*'`.
-Install eth or geth, npm and node.js (see https://docs.npmjs.com/getting-started/installing-node), then do:
++ Using `eth`: `eth -j --rpccorsdomain '*'`
- git clone https://github.com/ethereum/remix
- cd remix
- npm install && npm run build && npm run start_node
+**DO NOT DO EXECUTE THESE COMMANDS IF `geth`/`eth` STORES YOUR PRIVATE KEYS**, as any external system might be able to access your node's RPC server!
-open remix/index.html in your browser.
+Those commands will run the RPC server on `localhost:8545`, which is the default URL that Remix will connect to. This instance should **only** be used for debugging purposes. Never use features that could have an impact on your keys: do not unlock any keys, do not use this instance together with the wallet, do not activate the admin `web3` API.
-## REMIX First Step:
+### Run the debugger
-Once remix is connected to a node, you will be able to debug transactions.
-There's two way of doing that:
- - using a block number and a transaction index.
- - using a transaction hash.
-
-When loading the transaction succeed, the hash, from and to field will show up.
-Then the vm trace is loaded.
+See [here](remix-debugger/README.md) how to install, run and use the debugger locally.
The debugger itself contains several controls that allow stepping over the trace and seeing 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
-
-## CODING STYLE:
-
-Remix uses npm coding style: https://docs.npmjs.com/misc/coding-style
-Please be sure your code is compliant with this coding standard before sending PR.
-There's on the above page a bunch of links that propose integration with developer tools (Emacs, Atom, ...).
-You can also run 'npm run test' to check your local repository against the coding style.
+## Remix Modules
+
+Remix is built out of 3 different modules:
+
++ [`remix-solidity`](remix-solidity/README.md) provides Solidity analysis and decoding functions.
++ [`remix-lib`](remix-lib/README.md)
++ [`remix-debug`](remix-debugger/README.md) contains the debugger.
+
+## Contributing
+
+Everyone is very welcome to contribute on the codebase of Remix. Please reach us in [Gitter](https://gitter.im/ethereum/remix).
+
+For more information on the contributing procedure, see [CONTRIBUTING.md](CONTRIBUTING.md). For more information on running and developing the Remix debugger, see [the debugger README.md](remix-debugging/README.md).
diff --git a/ci/browser_tests.sh b/ci/browser_tests.sh
deleted file mode 100755
index 05feb26930..0000000000
--- a/ci/browser_tests.sh
+++ /dev/null
@@ -1,32 +0,0 @@
-#!/usr/bin/env bash
-
-
-SAUCECONNECT_URL="https://saucelabs.com/downloads/sc-4.3.16-linux.tar.gz"
-SAUCECONNECT_USERNAME="yanneth"
-SAUCECONNECT_ACCESSKEY="1f5a4560-b02b-41aa-b52b-f033aad30870"
-SAUCECONNECT_JOBIDENTIFIER="remix_tests_${TRAVIS_JOB_NUMBER}"
-SAUCECONNECT_READYFILE="sc.ready"
-TEST_EXITCODE=0
-
-npm run build
-npm run serve &
-
-wget $SAUCECONNECT_URL
-tar -zxvf sc-4.3.16-linux.tar.gz
-./sc-4.3.16-linux/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_firefox || TEST_EXITCODE=1
-npm run nightwatch_remote_chrome || TEST_EXITCODE=1
-npm run nightwatch_remote_safari || TEST_EXITCODE=1
-npm run nightwatch_remote_ie || 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/docs/Makefile b/docs/Makefile
new file mode 100644
index 0000000000..a04587262d
--- /dev/null
+++ b/docs/Makefile
@@ -0,0 +1,216 @@
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS =
+SPHINXBUILD = sphinx-build
+PAPER =
+BUILDDIR = _build
+
+# User-friendly check for sphinx-build
+ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
+$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
+endif
+
+# Internal variables.
+PAPEROPT_a4 = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+# the i18n builder cannot share the environment and doctrees with the others
+I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+
+.PHONY: help
+help:
+ @echo "Please use \`make ' where is one of"
+ @echo " html to make standalone HTML files"
+ @echo " dirhtml to make HTML files named index.html in directories"
+ @echo " singlehtml to make a single large HTML file"
+ @echo " pickle to make pickle files"
+ @echo " json to make JSON files"
+ @echo " htmlhelp to make HTML files and a HTML help project"
+ @echo " qthelp to make HTML files and a qthelp project"
+ @echo " applehelp to make an Apple Help Book"
+ @echo " devhelp to make HTML files and a Devhelp project"
+ @echo " epub to make an epub"
+ @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+ @echo " latexpdf to make LaTeX files and run them through pdflatex"
+ @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
+ @echo " text to make text files"
+ @echo " man to make manual pages"
+ @echo " texinfo to make Texinfo files"
+ @echo " info to make Texinfo files and run them through makeinfo"
+ @echo " gettext to make PO message catalogs"
+ @echo " changes to make an overview of all changed/added/deprecated items"
+ @echo " xml to make Docutils-native XML files"
+ @echo " pseudoxml to make pseudoxml-XML files for display purposes"
+ @echo " linkcheck to check all external links for integrity"
+ @echo " doctest to run all doctests embedded in the documentation (if enabled)"
+ @echo " coverage to run coverage check of the documentation (if enabled)"
+
+.PHONY: clean
+clean:
+ rm -rf $(BUILDDIR)/*
+
+.PHONY: html
+html:
+ $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+.PHONY: dirhtml
+dirhtml:
+ $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+.PHONY: singlehtml
+singlehtml:
+ $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
+ @echo
+ @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
+
+.PHONY: pickle
+pickle:
+ $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+ @echo
+ @echo "Build finished; now you can process the pickle files."
+
+.PHONY: json
+json:
+ $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+ @echo
+ @echo "Build finished; now you can process the JSON files."
+
+.PHONY: htmlhelp
+htmlhelp:
+ $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+ @echo
+ @echo "Build finished; now you can run HTML Help Workshop with the" \
+ ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+.PHONY: qthelp
+qthelp:
+ $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+ @echo
+ @echo "Build finished; now you can run "qcollectiongenerator" with the" \
+ ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+ @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Remix.qhcp"
+ @echo "To view the help file:"
+ @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Remix.qhc"
+
+.PHONY: applehelp
+applehelp:
+ $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp
+ @echo
+ @echo "Build finished. The help book is in $(BUILDDIR)/applehelp."
+ @echo "N.B. You won't be able to view it unless you put it in" \
+ "~/Library/Documentation/Help or install it in your application" \
+ "bundle."
+
+.PHONY: devhelp
+devhelp:
+ $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
+ @echo
+ @echo "Build finished."
+ @echo "To view the help file:"
+ @echo "# mkdir -p $$HOME/.local/share/devhelp/Remix"
+ @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Remix"
+ @echo "# devhelp"
+
+.PHONY: epub
+epub:
+ $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
+ @echo
+ @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
+
+.PHONY: latex
+latex:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo
+ @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+ @echo "Run \`make' in that directory to run these through (pdf)latex" \
+ "(use \`make latexpdf' here to do that automatically)."
+
+.PHONY: latexpdf
+latexpdf:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo "Running LaTeX files through pdflatex..."
+ $(MAKE) -C $(BUILDDIR)/latex all-pdf
+ @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+.PHONY: latexpdfja
+latexpdfja:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo "Running LaTeX files through platex and dvipdfmx..."
+ $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
+ @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+.PHONY: text
+text:
+ $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
+ @echo
+ @echo "Build finished. The text files are in $(BUILDDIR)/text."
+
+.PHONY: man
+man:
+ $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
+ @echo
+ @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
+
+.PHONY: texinfo
+texinfo:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo
+ @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
+ @echo "Run \`make' in that directory to run these through makeinfo" \
+ "(use \`make info' here to do that automatically)."
+
+.PHONY: info
+info:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo "Running Texinfo files through makeinfo..."
+ make -C $(BUILDDIR)/texinfo info
+ @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
+
+.PHONY: gettext
+gettext:
+ $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
+ @echo
+ @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
+
+.PHONY: changes
+changes:
+ $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+ @echo
+ @echo "The overview file is in $(BUILDDIR)/changes."
+
+.PHONY: linkcheck
+linkcheck:
+ $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+ @echo
+ @echo "Link check complete; look for any errors in the above output " \
+ "or in $(BUILDDIR)/linkcheck/output.txt."
+
+.PHONY: doctest
+doctest:
+ $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+ @echo "Testing of doctests in the sources finished, look at the " \
+ "results in $(BUILDDIR)/doctest/output.txt."
+
+.PHONY: coverage
+coverage:
+ $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage
+ @echo "Testing of coverage in the sources finished, look at the " \
+ "results in $(BUILDDIR)/coverage/python.txt."
+
+.PHONY: xml
+xml:
+ $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
+ @echo
+ @echo "Build finished. The XML files are in $(BUILDDIR)/xml."
+
+.PHONY: pseudoxml
+pseudoxml:
+ $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
+ @echo
+ @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
diff --git a/docs/analysis_tab.md b/docs/analysis_tab.md
new file mode 100644
index 0000000000..8c82d0e5f1
--- /dev/null
+++ b/docs/analysis_tab.md
@@ -0,0 +1,33 @@
+Analysis
+========
+
+This section gives information about the last compilation. By default, a
+new analysis is run at each compilation.
+
+The analysis tab gives detailed information about the contract code. It
+can help you avoid code mistakes and to enforce best practices.
+
+
+
+Here is the list of analyzers:
+
+Security:
+- Transaction origin: Warns if tx.origin is used
+- Check effects: Avoid potential reentrancy bugs
+- Inline assembly: Use of Inline Assembly
+- Block timestamp: Semantics maybe unclear
+- Low level calls: Semantics maybe unclear
+- Block.blockhash usage: Semantics maybe unclear
+
+
+Gas & Economy:
+- Gas costs: Warns if the gas requirements of the functions
+ are too high
+- This on local calls: Invocation of local functions via
+ this
+
+Miscellaneous:
+- Constant functions: Checks for potentially constant
+ functions
+- Similar variable names: Checks if variable names are too
+ similar
diff --git a/docs/code_contribution_guide.md b/docs/code_contribution_guide.md
new file mode 100644
index 0000000000..ad647df40e
--- /dev/null
+++ b/docs/code_contribution_guide.md
@@ -0,0 +1,12 @@
+Code contribution guide
+=======================
+
+Remix is an open source tool and we encourage anyone to help us improve our tool.
+You can do that by opening issues, giving feedback or by contributing a pull request
+to our codebase.
+
+Remix application is built with Javascript and it doesn't use any framework, we only
+rely on selected set of NPM modules, like `yo-yo`, `csjs-inject` and others. Check out
+package.json file to learn more about the stack.
+
+To learn more, please visit our [Github page](https://github.com/ethereum/remix-ide).
diff --git a/docs/community.md b/docs/community.md
new file mode 100644
index 0000000000..9ffd1553af
--- /dev/null
+++ b/docs/community.md
@@ -0,0 +1,14 @@
+Community
+=======================
+
+We know that blockchain ecosystem is very new and that lots of information is scattered around the web.
+That is why we created a community support channel where we and other users try to answer your questions if
+you get stuck using Remix. Please, join [the community](https://gitter.im/ethereum/remix) and ask community for help.
+
+For anyone who is interested in developing a custom plugin for Remix or who wants to contribute to the codebase,
+we opened [contributors' channel](https://gitter.im/ethereum/remix-dev) specially for developers working on Remix tool.
+
+We would kindly ask you to respect the space and to use it for
+getting help with your work and the developers' channel for discussions related to working on Remix codebase. If you have
+ideas for collaborations or you want to promote your project, try to find some more appropriate channels to do so. Or you can contact
+main contributors directly on Gitter or Twitter.
diff --git a/docs/compile_tab.md b/docs/compile_tab.md
new file mode 100644
index 0000000000..55b7452da9
--- /dev/null
+++ b/docs/compile_tab.md
@@ -0,0 +1,28 @@
+Compiling contracts
+===================
+
+By default Remix triggers a compilation each time the current file is
+changed or another file is selected. If the contract has a lot of
+dependencies and takes a long time to compile, it is possible to disable
+the autocompilation.
+
+
+
+After each compilation, a list is updated with all the newly compiled
+contracts.
+
+Details modal dialog displays detailed information about the current
+selected contract.
+
+From this tab, you can also publish your contract to Swarm (only non
+abstract contracts can be published).
+
+Published data notably contains the `abi` and solidity source code.
+
+After a contract is published, you can find its metadata information
+using the bzz URL located in the details modal dialog `SWARM LOCATION`.
+
+Compilation Errors and Warning are displayed below the contract section.
+At each compilation, the static analysis tab builds a report. It is very
+valuable when addressing reported issues even if the compiler doesn't
+complain. ([see more](http://remix.readthedocs.io/en/latest/analysis_tab.html))
diff --git a/docs/conf.py b/docs/conf.py
new file mode 100644
index 0000000000..4e5b322071
--- /dev/null
+++ b/docs/conf.py
@@ -0,0 +1,305 @@
+# -*- coding: utf-8 -*-
+#
+# Remix documentation build configuration file, created by
+# sphinx-quickstart on Mon Feb 20 12:16:16 2017.
+#
+# This file is execfile()d with the current directory set to its
+# containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys
+import os
+
+
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+# sys.path.insert(0, os.path.abspath('.'))
+
+# -- General configuration ------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = []
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix(es) of source filenames.
+# You can specify multiple suffix as a list of string:
+# source_suffix = ['.rst', '.md']
+
+
+# The encoding of source files.
+#source_encoding = 'utf-8-sig'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'Remix, Ethereum-IDE'
+copyright = u'2018, Remix'
+author = u'Remix team'
+
+github_doc_root = 'https://github.com/ethereum/remix/tree/master/docs/'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = u'1'
+# The full version, including alpha/beta/rc tags.
+release = u'1'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#
+# This is also used if you do content translation via gettext catalogs.
+# Usually you set "language" from the command line for these cases.
+language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+exclude_patterns = ['_build']
+
+# The reST default role (used for this markup: `text`) to use for all
+# documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+highlight_language = 'JavaScript'
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+# If true, keep warnings as "system message" paragraphs in the built documents.
+#keep_warnings = False
+
+# If true, `todo` and `todoList` produce output, else they produce nothing.
+todo_include_todos = False
+
+
+# -- Options for HTML output ----------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+html_theme = 'sphinx_rtd_theme'
+
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further. For a list of options available for each theme, see the
+# documentation.
+#html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+#html_theme_path = []
+
+# The name for this set of Sphinx documents. If None, it defaults to
+# " v documentation".
+#html_title = None
+
+# A shorter title for the navigation bar. Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (relative to this directory) to use as a favicon of
+# the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# Add any extra paths that contain custom files (such as robots.txt or
+# .htaccess) here, relative to this directory. These files are copied
+# directly to the root of the documentation.
+#html_extra_path = []
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_domain_indices = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#html_show_sourcelink = True
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+#html_show_sphinx = True
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+#html_show_copyright = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a tag referring to it. The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = None
+
+# Language to be used for generating the HTML full-text search index.
+# Sphinx supports the following languages:
+# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja'
+# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr'
+#html_search_language = 'en'
+
+# A dictionary with options for the search language support, empty by default.
+# Now only 'ja' uses this config value
+#html_search_options = {'type': 'default'}
+
+# The name of a javascript file (relative to the configuration directory) that
+# implements a search results scorer. If empty, the default will be used.
+#html_search_scorer = 'scorer.js'
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'Remixdoc'
+
+# -- Options for LaTeX output ---------------------------------------------
+
+latex_elements = {
+# The paper size ('letterpaper' or 'a4paper').
+#'papersize': 'letterpaper',
+
+# The font size ('10pt', '11pt' or '12pt').
+#'pointsize': '10pt',
+
+# Additional stuff for the LaTeX preamble.
+#'preamble': '',
+
+# Latex figure (float) alignment
+#'figure_align': 'htbp',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title,
+# author, documentclass [howto, manual, or own class]).
+latex_documents = [
+ (master_doc, 'Remix.tex', u'Remix Documentation',
+ u'yann300', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# If true, show page references after internal links.
+#latex_show_pagerefs = False
+
+# If true, show URL addresses after external links.
+#latex_show_urls = False
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_domain_indices = True
+
+
+# -- Options for manual page output ---------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+ (master_doc, 'remix', u'Remix Documentation',
+ [author], 1)
+]
+
+# If true, show URL addresses after external links.
+#man_show_urls = False
+
+
+# -- Options for Texinfo output -------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+# dir menu entry, description, category)
+texinfo_documents = [
+ (master_doc, 'Remix', u'Remix Documentation',
+ author, 'Remix', 'One line description of project.',
+ 'Miscellaneous'),
+]
+
+# Documents to append as an appendix to all manuals.
+#texinfo_appendices = []
+
+# If false, no module index is generated.
+#texinfo_domain_indices = True
+
+# How to display URL addresses: 'footnote', 'no', or 'inline'.
+#texinfo_show_urls = 'footnote'
+
+# If true, do not generate a @detailmenu in the "Top" node's menu.
+#texinfo_no_detailmenu = False
+
+from recommonmark.parser import CommonMarkParser
+source_suffix = ['.rst', '.md']
+source_parsers = {'.md': CommonMarkParser}
+
+
+# app setup hook
+# def setup(app):
+# app.add_config_value('recommonmark_config', {
+# 'url_resolver': lambda url: github_doc_root + url,
+# 'enable_auto_toc_tree': True,
+# 'enable_eval_rst': True,
+# 'enable_auto_doc_ref': True,
+# }, True)
+# app.add_transform(AutoStructify)
diff --git a/docs/debugger_tab.md b/docs/debugger_tab.md
new file mode 100644
index 0000000000..004941baf1
--- /dev/null
+++ b/docs/debugger_tab.md
@@ -0,0 +1,12 @@
+Debugging
+=========
+
+This module allows you to debug the transaction. It can be used to
+deploy transactions created from Remix and already mined transactions.
+(debugging works only if the current environment provides the necessary
+features).
+
+
+
+For more information about debugging, see the [Tutorial on debugging transactions with Remix
+](http://remix.readthedocs.io/en/latest/tutorial_debug.html)
diff --git a/docs/file_explorer.md b/docs/file_explorer.md
new file mode 100644
index 0000000000..cabf680e5d
--- /dev/null
+++ b/docs/file_explorer.md
@@ -0,0 +1,52 @@
+File Explorer
+=============
+
+The file explorer lists by default all the files stored in your browser.
+You can see them in the browser folder. You can always rename, remove or
+add new files to the file explorer.
+
+
+
+Note that clearing the browser storage will permanently delete all the
+solidity files you wrote. To avoid this, you can use Remixd, which
+enables you to store and sync files in the browser with your local
+computer (for more information see ../tutorial\_remixd\_filesystem)
+
+
+
+We will start by reviewing at the icons at the top left - from left to
+the right:
+
+Create new File
+---------------
+
+Creates a new `untitled.sol` file in Remix.
+
+Add Local File
+--------------
+
+Allows you to select files from the local file system and import them to
+the Remix browser storage.
+
+Publish to Gist
+---------------
+
+Publishes all files from the browser folder to a gist.
+Gist API has changed in 2018 and it unfortunately requires users to be authenticated to be able to publish a gist.
+
+Click [this link](https://github.com/settings/tokens) to Github tokens setup and select Generate new token.
+Then check only Create gists checkbox and generate a new token.
+
+Then paste it in Remix (right panel/Settings tab) and click Save. Now you should be able to use the feature.
+
+Copy to another Remix instance
+------------------------
+
+Enables you to copy files from the browser storage to another instance
+(URL) of Remix.
+
+Connect your filesystem to Remix
+--------------------
+
+Allows to sync between Remix and your local file system (see
+[more about RemixD](http://remix.readthedocs.io/en/latest/tutorial_remixd_filesystem.html)).
diff --git a/docs/images/remix_analysistab.png b/docs/images/remix_analysistab.png
new file mode 100755
index 0000000000..e43143962c
Binary files /dev/null and b/docs/images/remix_analysistab.png differ
diff --git a/docs/images/remix_compiletab.png b/docs/images/remix_compiletab.png
new file mode 100755
index 0000000000..cd51b11574
Binary files /dev/null and b/docs/images/remix_compiletab.png differ
diff --git a/docs/images/remix_debuggertab.png b/docs/images/remix_debuggertab.png
new file mode 100755
index 0000000000..104d9744b2
Binary files /dev/null and b/docs/images/remix_debuggertab.png differ
diff --git a/docs/images/remix_editor.png b/docs/images/remix_editor.png
new file mode 100755
index 0000000000..9260f05fe4
Binary files /dev/null and b/docs/images/remix_editor.png differ
diff --git a/docs/images/remix_file_explorer_browser.png b/docs/images/remix_file_explorer_browser.png
new file mode 100755
index 0000000000..3f09c0c752
Binary files /dev/null and b/docs/images/remix_file_explorer_browser.png differ
diff --git a/docs/images/remix_file_explorer_menu.png b/docs/images/remix_file_explorer_menu.png
new file mode 100755
index 0000000000..2140e9b36c
Binary files /dev/null and b/docs/images/remix_file_explorer_menu.png differ
diff --git a/docs/images/remix_quickstart_javascriptvm_callinginstance.png b/docs/images/remix_quickstart_javascriptvm_callinginstance.png
new file mode 100644
index 0000000000..fe11a2cb06
Binary files /dev/null and b/docs/images/remix_quickstart_javascriptvm_callinginstance.png differ
diff --git a/docs/images/remix_quickstart_javascriptvm_creation.png b/docs/images/remix_quickstart_javascriptvm_creation.png
new file mode 100644
index 0000000000..fc7598aea7
Binary files /dev/null and b/docs/images/remix_quickstart_javascriptvm_creation.png differ
diff --git a/docs/images/remix_quickstart_javascriptvm_creationTransaction.png b/docs/images/remix_quickstart_javascriptvm_creationTransaction.png
new file mode 100644
index 0000000000..0b27bc105f
Binary files /dev/null and b/docs/images/remix_quickstart_javascriptvm_creationTransaction.png differ
diff --git a/docs/images/remix_recorder.png b/docs/images/remix_recorder.png
new file mode 100644
index 0000000000..1f7abc7363
Binary files /dev/null and b/docs/images/remix_recorder.png differ
diff --git a/docs/images/remix_runtab.png b/docs/images/remix_runtab.png
new file mode 100755
index 0000000000..c636298493
Binary files /dev/null and b/docs/images/remix_runtab.png differ
diff --git a/docs/images/remix_runtab_example.png b/docs/images/remix_runtab_example.png
new file mode 100755
index 0000000000..78fba7e994
Binary files /dev/null and b/docs/images/remix_runtab_example.png differ
diff --git a/docs/images/remix_settingstab.png b/docs/images/remix_settingstab.png
new file mode 100755
index 0000000000..bfd50332e1
Binary files /dev/null and b/docs/images/remix_settingstab.png differ
diff --git a/docs/images/remix_supporttab.png b/docs/images/remix_supporttab.png
new file mode 100755
index 0000000000..b62ead496b
Binary files /dev/null and b/docs/images/remix_supporttab.png differ
diff --git a/docs/images/remix_terminal.png b/docs/images/remix_terminal.png
new file mode 100755
index 0000000000..3f096a2490
Binary files /dev/null and b/docs/images/remix_terminal.png differ
diff --git a/docs/index.md b/docs/index.md
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/docs/index.rst b/docs/index.rst
new file mode 100644
index 0000000000..10e3b25db9
--- /dev/null
+++ b/docs/index.rst
@@ -0,0 +1,72 @@
+Welcome to Remix documentation!
+===============================
+
+Remix is a powerful, open source tool that helps you write Solidity contracts straight from the browser.
+Written in Javascript, Remix supports both usage in the browser or locally.
+
+Remix also supports testing, debugging and deploying of smart contracts and much more.
+
+Our Remix project with all its features is available
+at `remix.ethereum.org `__ and more information can be found in these
+docs. Our tool is available at `our GitHub repository
+`__.
+
+This set of documents covers instructions on how to use Remix and some tutorials to help you get started.
+
+Userful links:
+
+- `Solidity documentation `__
+
+- `Remix alpha `__ - version where we test new Remix release (not stable!)
+
+- `Ethereum Stackexchange for Remix `__
+
+- `Community support channel `__
+
+- `Dapp Developer resources (Ethereum wiki) `__
+
+
+.. toctree::
+ :maxdepth: 2
+ :caption: Quick start
+
+ packages
+ solidity_editor
+ compile_tab
+ quickstart_javascript_vm
+ settings_tab
+
+
+.. toctree::
+ :maxdepth: 2
+ :caption: Deploy and test
+
+ run_tab
+ udapp
+
+.. toctree::
+ :maxdepth: 2
+ :caption: Other Remix features
+
+ file_explorer
+ debugger_tab
+ analysis_tab
+ terminal
+
+.. toctree::
+ :maxdepth: 2
+ :caption: Tutorials and workshops
+
+ workshop_Building_smart_contracts_with_Remix
+ tutorial_remixd_filesystem
+ tutorial_debug
+ tutorial_import
+ tutorial_mist
+
+.. toctree::
+ :maxdepth: 2
+ :caption: Code contribution guide
+
+ code_contribution_guide
+ Community
+ support_tab
diff --git a/docs/make.bat b/docs/make.bat
new file mode 100755
index 0000000000..963594cac0
--- /dev/null
+++ b/docs/make.bat
@@ -0,0 +1,263 @@
+@ECHO OFF
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+ set SPHINXBUILD=sphinx-build
+)
+set BUILDDIR=_build
+set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
+set I18NSPHINXOPTS=%SPHINXOPTS% .
+if NOT "%PAPER%" == "" (
+ set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
+ set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
+)
+
+if "%1" == "" goto help
+
+if "%1" == "help" (
+ :help
+ echo.Please use `make ^` where ^ is one of
+ echo. html to make standalone HTML files
+ echo. dirhtml to make HTML files named index.html in directories
+ echo. singlehtml to make a single large HTML file
+ echo. pickle to make pickle files
+ echo. json to make JSON files
+ echo. htmlhelp to make HTML files and a HTML help project
+ echo. qthelp to make HTML files and a qthelp project
+ echo. devhelp to make HTML files and a Devhelp project
+ echo. epub to make an epub
+ echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
+ echo. text to make text files
+ echo. man to make manual pages
+ echo. texinfo to make Texinfo files
+ echo. gettext to make PO message catalogs
+ echo. changes to make an overview over all changed/added/deprecated items
+ echo. xml to make Docutils-native XML files
+ echo. pseudoxml to make pseudoxml-XML files for display purposes
+ echo. linkcheck to check all external links for integrity
+ echo. doctest to run all doctests embedded in the documentation if enabled
+ echo. coverage to run coverage check of the documentation if enabled
+ goto end
+)
+
+if "%1" == "clean" (
+ for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
+ del /q /s %BUILDDIR%\*
+ goto end
+)
+
+
+REM Check if sphinx-build is available and fallback to Python version if any
+%SPHINXBUILD% 1>NUL 2>NUL
+if errorlevel 9009 goto sphinx_python
+goto sphinx_ok
+
+:sphinx_python
+
+set SPHINXBUILD=python -m sphinx.__init__
+%SPHINXBUILD% 2> nul
+if errorlevel 9009 (
+ echo.
+ echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
+ echo.installed, then set the SPHINXBUILD environment variable to point
+ echo.to the full path of the 'sphinx-build' executable. Alternatively you
+ echo.may add the Sphinx directory to PATH.
+ echo.
+ echo.If you don't have Sphinx installed, grab it from
+ echo.http://sphinx-doc.org/
+ exit /b 1
+)
+
+:sphinx_ok
+
+
+if "%1" == "html" (
+ %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/html.
+ goto end
+)
+
+if "%1" == "dirhtml" (
+ %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
+ goto end
+)
+
+if "%1" == "singlehtml" (
+ %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
+ goto end
+)
+
+if "%1" == "pickle" (
+ %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can process the pickle files.
+ goto end
+)
+
+if "%1" == "json" (
+ %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can process the JSON files.
+ goto end
+)
+
+if "%1" == "htmlhelp" (
+ %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can run HTML Help Workshop with the ^
+.hhp project file in %BUILDDIR%/htmlhelp.
+ goto end
+)
+
+if "%1" == "qthelp" (
+ %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can run "qcollectiongenerator" with the ^
+.qhcp project file in %BUILDDIR%/qthelp, like this:
+ echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Remix.qhcp
+ echo.To view the help file:
+ echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Remix.ghc
+ goto end
+)
+
+if "%1" == "devhelp" (
+ %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished.
+ goto end
+)
+
+if "%1" == "epub" (
+ %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The epub file is in %BUILDDIR%/epub.
+ goto end
+)
+
+if "%1" == "latex" (
+ %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
+ goto end
+)
+
+if "%1" == "latexpdf" (
+ %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+ cd %BUILDDIR%/latex
+ make all-pdf
+ cd %~dp0
+ echo.
+ echo.Build finished; the PDF files are in %BUILDDIR%/latex.
+ goto end
+)
+
+if "%1" == "latexpdfja" (
+ %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+ cd %BUILDDIR%/latex
+ make all-pdf-ja
+ cd %~dp0
+ echo.
+ echo.Build finished; the PDF files are in %BUILDDIR%/latex.
+ goto end
+)
+
+if "%1" == "text" (
+ %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The text files are in %BUILDDIR%/text.
+ goto end
+)
+
+if "%1" == "man" (
+ %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The manual pages are in %BUILDDIR%/man.
+ goto end
+)
+
+if "%1" == "texinfo" (
+ %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
+ goto end
+)
+
+if "%1" == "gettext" (
+ %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
+ goto end
+)
+
+if "%1" == "changes" (
+ %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.The overview file is in %BUILDDIR%/changes.
+ goto end
+)
+
+if "%1" == "linkcheck" (
+ %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Link check complete; look for any errors in the above output ^
+or in %BUILDDIR%/linkcheck/output.txt.
+ goto end
+)
+
+if "%1" == "doctest" (
+ %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Testing of doctests in the sources finished, look at the ^
+results in %BUILDDIR%/doctest/output.txt.
+ goto end
+)
+
+if "%1" == "coverage" (
+ %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Testing of coverage in the sources finished, look at the ^
+results in %BUILDDIR%/coverage/python.txt.
+ goto end
+)
+
+if "%1" == "xml" (
+ %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The XML files are in %BUILDDIR%/xml.
+ goto end
+)
+
+if "%1" == "pseudoxml" (
+ %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.
+ goto end
+)
+
+:end
diff --git a/docs/mist1.png b/docs/mist1.png
new file mode 100644
index 0000000000..cd2991e2fd
Binary files /dev/null and b/docs/mist1.png differ
diff --git a/docs/packages.md b/docs/packages.md
new file mode 100644
index 0000000000..ba8440c424
--- /dev/null
+++ b/docs/packages.md
@@ -0,0 +1,13 @@
+Packages
+========
+
+This part focuses on using `Remix IDE`, which is a browser based smart contract IDE. We will basically answer to the question:
+Where can I use / download `Remix IDE`, and what is the difference between packages?
+
+- An online version is available at [https://remix.ethereum.org](https://remix.ethereum.org). This version is stable and is updated at almost every release.
+- An alpha online version is available at [https://remix-alpha.ethereum.org](https://remix-alpha.ethereum.org). This is not a stable version.
+- npm `remix-ide` package `npm install remix-ide -g`. `remix-ide` create a new instance of `Remix IDE` available at [http://127.0.0.1:8080](http://127.0.0.1:8080) and make the current folder available to Remix IDE by automatically starting `remixd`.
+see [Connection to `remixd`](http://remix.readthedocs.io/en/latest/tutorial_remixd_filesystem.html) for more information about sharing local file with `Remix IDE`.
+- Github release: [https://github.com/ethereum/remix-ide/releases](https://github.com/ethereum/remix-ide/releases) . The source code is packaged at every release but still need to be built using `npm run build`.
+- Mist: `Remix IDE` can be started and use the local geth node from `Mist` [https://github.com/ethereum/mist/releases](https://github.com/ethereum/mist/releases)
+- Electron: `Remix IDE` wrapped as an Electron app is available at [https://github.com/horizon-games/remix-app](https://github.com/horizon-games/remix-app)
\ No newline at end of file
diff --git a/docs/quickstart_javascript_vm.md b/docs/quickstart_javascript_vm.md
new file mode 100644
index 0000000000..a9abe8ee1f
--- /dev/null
+++ b/docs/quickstart_javascript_vm.md
@@ -0,0 +1,104 @@
+Quick Start using the JavaScript VM
+===================================
+
+There are 3 type of environments Remix can be plugged to:
+`Javascript VM`, `Injected provider`, or `Web3 provider`. (for details see [Running transactions](http://remix.readthedocs.io/en/latest/run_tab.html))
+
+Both `Web3 provider` and `Injected provider` require the use of an
+external tool.
+
+The external tool for `Web3 provider` is an Ethereum node the tools for
+`Injected provider` are Mist or Metamask.
+
+The `JavaScript VM` mode is convenient because each execution runs in
+your browser. Thus reloading the page will restart Remix with an empty
+state.
+
+So for performance purposes, it might also be better to use an external
+node.
+
+Selecting the VM mode
+---------------------
+
+Make sure the VM mode is selected. All accounts displayed in `Accounts`
+should have 100 ether.
+
+Sample contract
+---------------
+
+``` {.sourceCode .none}
+pragma solidity ^0.4.16;
+
+contract testContract {
+
+ uint value;
+ function testContract(uint _p) {
+ value = _p;
+ }
+
+ function setP(uint _n) payable {
+ value = _n;
+ }
+
+ function setNP(uint _n) {
+ value = _n;
+ }
+
+ function get () constant returns (uint) {
+ return value;
+ }
+}
+```
+
+This contract is very basic. The goal is to quickly start to create and
+to interact with a sample contract.
+
+Deploying an instance
+---------------------
+
+The `Compile tab` displays information related to the current contract
+(note that there can be more than one) (see ../compile\_tab).
+
+Moving on, in the `Run tab` select, `JavaScript VM` to specify that you
+are going to deploy an instance of the contract in the `JavaScript VM`
+state.
+
+
+
+The constructor of `testContract` needs a parameter (of type `uint`).
+Give any value and click on `Create`.
+
+The transaction which deploys the instance of `testContract` is created.
+
+In a "normal" blockchain, it can take several seconds to execute. This
+is the time for the transaction to be mined. However, because we are
+using the `JavaScript VM`, our execution is immediate.
+
+The terminal will inform you about the transaction. You can see details
+there and start debugging.
+
+The newly created instance is displayed in the `run tab`.
+
+
+
+Interacting with an instance
+----------------------------
+
+This new instance contains 3 actions which corresponds to the 3
+functions (`setP`, `setPN`, `get`). Clicking on `SetP` or `SetPN` will
+create a new transaction.
+
+Note that `SetP` is `payable` (red action) : it is possible to send
+value (Ether) to the contract.
+
+`SetPN` is not payable (light red action) : it is not possible to send
+value (Ether) to the contract.
+
+Clicking on `get` will not execute a transaction (blue action). It is
+not necessary to do so because `get` does not modify the state (variable
+`value`) of this instance.
+
+As `get` is `constant` you can see the return value just below the
+action.
+
+
diff --git a/docs/remix1.png b/docs/remix1.png
new file mode 100644
index 0000000000..7575d34f6c
Binary files /dev/null and b/docs/remix1.png differ
diff --git a/docs/remix2.png b/docs/remix2.png
new file mode 100644
index 0000000000..17a53affd9
Binary files /dev/null and b/docs/remix2.png differ
diff --git a/docs/remix3.png b/docs/remix3.png
new file mode 100644
index 0000000000..060352a755
Binary files /dev/null and b/docs/remix3.png differ
diff --git a/docs/remix4.png b/docs/remix4.png
new file mode 100644
index 0000000000..404ed50572
Binary files /dev/null and b/docs/remix4.png differ
diff --git a/docs/remix5.png b/docs/remix5.png
new file mode 100644
index 0000000000..4297d58adc
Binary files /dev/null and b/docs/remix5.png differ
diff --git a/docs/remix_breakpoint.png b/docs/remix_breakpoint.png
new file mode 100644
index 0000000000..c755f61348
Binary files /dev/null and b/docs/remix_breakpoint.png differ
diff --git a/docs/remix_debuginstructions.png b/docs/remix_debuginstructions.png
new file mode 100644
index 0000000000..bb36decbaf
Binary files /dev/null and b/docs/remix_debuginstructions.png differ
diff --git a/docs/remix_debugtransactioninfo.png b/docs/remix_debugtransactioninfo.png
new file mode 100644
index 0000000000..92d2e33100
Binary files /dev/null and b/docs/remix_debugtransactioninfo.png differ
diff --git a/docs/remix_enterdebugsession.png b/docs/remix_enterdebugsession.png
new file mode 100644
index 0000000000..77acc520ee
Binary files /dev/null and b/docs/remix_enterdebugsession.png differ
diff --git a/docs/remix_executionexception.png b/docs/remix_executionexception.png
new file mode 100644
index 0000000000..9f28396b8a
Binary files /dev/null and b/docs/remix_executionexception.png differ
diff --git a/docs/remix_navigation.png b/docs/remix_navigation.png
new file mode 100644
index 0000000000..d4187a462a
Binary files /dev/null and b/docs/remix_navigation.png differ
diff --git a/docs/remix_soliditylocals.png b/docs/remix_soliditylocals.png
new file mode 100644
index 0000000000..bfa627d1a4
Binary files /dev/null and b/docs/remix_soliditylocals.png differ
diff --git a/docs/remix_soliditystate.png b/docs/remix_soliditystate.png
new file mode 100644
index 0000000000..789df079e2
Binary files /dev/null and b/docs/remix_soliditystate.png differ
diff --git a/docs/remix_startdebugging.png b/docs/remix_startdebugging.png
new file mode 100644
index 0000000000..bf5287a5c3
Binary files /dev/null and b/docs/remix_startdebugging.png differ
diff --git a/docs/remix_stepdetail.png b/docs/remix_stepdetail.png
new file mode 100644
index 0000000000..efde21304e
Binary files /dev/null and b/docs/remix_stepdetail.png differ
diff --git a/docs/remix_valueinput.png b/docs/remix_valueinput.png
new file mode 100644
index 0000000000..381ebfc86a
Binary files /dev/null and b/docs/remix_valueinput.png differ
diff --git a/docs/remixd_alert.png b/docs/remixd_alert.png
new file mode 100644
index 0000000000..d64e6f7214
Binary files /dev/null and b/docs/remixd_alert.png differ
diff --git a/docs/remixd_connectionok.png b/docs/remixd_connectionok.png
new file mode 100644
index 0000000000..e8381f55df
Binary files /dev/null and b/docs/remixd_connectionok.png differ
diff --git a/docs/remixd_noconnection.png b/docs/remixd_noconnection.png
new file mode 100644
index 0000000000..b6cd6f5b40
Binary files /dev/null and b/docs/remixd_noconnection.png differ
diff --git a/docs/run_tab.md b/docs/run_tab.md
new file mode 100644
index 0000000000..f13fb6591f
--- /dev/null
+++ b/docs/run_tab.md
@@ -0,0 +1,278 @@
+Running transactions
+====================
+
+The Run tab is an important section of Remix. It allows you to send
+transactions to the current environment.
+
+
+
+Run Setup
+---------
+
+The following settings allow you to directly influence the transaction
+execution:
+
+Environment:
+
+- `JavaScript VM`: All the transactions will be executed in
+ a sandbox blockchain in the browser. This means nothing
+ will be persisted and a page reload will restart a new
+ blockchain from scratch, the old one will not be saved.
+
+- `Injected Provider`: Remix will connect to an injected
+ web3 provider. `Mist` and `Metamask` are example of
+ providers that inject web3, thus can be used with this
+ option.
+
+- `Web3 Provider`: Remix will connect to a remote node. You
+ will need to provide the URL address to the selected
+ provider: geth, parity or any Ethereum client.
+
+- Account: the list of accounts associated with the current
+ environment (and their associated balances).
+
+- Gas Limit: the maximum amount of gas that can be set for all the
+ transactions created in Remix.
+
+- Value: the amount of value for the next created transaction (this
+ value is always reset to 0 after each transaction execution).
+
+ 
+
+Initiate Instance
+-----------------
+
+This section contains the list of compiled contracts and 2 actions:
+
+- `At Address` assumes the given address is an instance of the
+ selected contract. It is then possible to interact with an already
+ deployed contract. There's no check at this point, so be careful
+ when using this feature, and be sure you trust the contract at that
+ address.
+
+- `Create` send a transaction that deploys the selected contract. When
+ the transaction is mined, the newly created instance will be added
+ (this might take several seconds). Note that if the `constructor`
+ has parameters, you need to specify them.
+
+Pending Instances
+-----------------
+
+Validating a transaction take several seconds. During this time, the GUI
+shows it in a pending mode. When transaction is mined the number of
+pending transactions is updated and the transaction is added to the log
+(see ../terminal)
+
+USING ABI
+---------
+
+Using `Deploy` or `At Address` is a classic use case of Remix. It is
+possible though to interact with a contract by using its ABI. The ABI is
+a JSON array which describe its interface.
+
+To interact with a contract using the ABI, create a new file in Remix
+with extension `*.abi` and copy the ABI content to it. Then in the input
+next to `At Address`, put the Address of the contract you want to
+interact with. Click on `At Address`, a new "connection" with the
+contract will popup below.
+
+USING THE RECORDER
+------------------
+
+The Recorder allows to save a bunch of transactions in a JSON file and
+rerun them later either in the same environment or in another.
+
+Saving to JSON allows to easily check the transaction list, tweak input
+parameters, change linked library, etc...
+
+We can find many use cases for the recorder, for instance:
+: - After having coded and tested contracts in a constrained
+ environment (like the JavaScript VM), it could be interesting to
+ redeploy them easily in a more persisted environment (like a
+ Geth node) in order to check whether everything behaves normally
+ in a classic environment.
+ - Deploying contract does often require more than creating one
+ transaction.
+ - Working in a dev environment does often require to setup the
+ state in a first place.
+
+
+
+Saving a record ends up with the creation of this type of content (see
+below):
+
+In that specific record, 3 transactions are executed:
+
+The first corresponds to the deployment of the lib `testLib`.
+
+The second corresponds to the deployment of the contract `test`, the
+first parameter of the constructor is set to 11. That contract depends
+on a library. The linkage is done using the property `linkReferences`.
+In that case we use the addres of the previously created library :
+`created{1512830014773}`. the number is the id (timestamp) of the
+transaction that leads to the creation of the library.
+
+The third parameter corresponds to the call to the function `set` of the
+contract `test` (the property to is set to: `created{1512830015080}`) .
+Input parameters are `1` and
+`0xca35b7d915458ef540ade6068dfe2f44e8fa733c`
+
+all these transactions are created using the value of the accounts
+`account{0}`.
+
+``` {.sourceCode .none}
+{
+"accounts": {
+ "account{0}": "0xca35b7d915458ef540ade6068dfe2f44e8fa733c"
+},
+"linkReferences": {
+ "testLib": "created{1512830014773}"
+},
+"transactions": [
+ {
+ "timestamp": 1512830014773,
+ "record": {
+ "value": "0",
+ "parameters": [],
+ "abi": "0xbc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a",
+ "contractName": "testLib",
+ "bytecode": "60606040523415600e57600080fd5b60968061001c6000396000f300606060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680636d4ce63c146044575b600080fd5b604a6060565b6040518082815260200191505060405180910390f35b6000610d809050905600a165627a7a7230582022d123b15248b8176151f8d45c2dc132063bcc9bb8d5cd652aea7efae362c8050029",
+ "linkReferences": {},
+ "type": "constructor",
+ "from": "account{0}"
+ }
+ },
+ {
+ "timestamp": 1512830015080,
+ "record": {
+ "value": "100",
+ "parameters": [
+ 11
+ ],
+ "abi": "0xc41589e7559804ea4a2080dad19d876a024ccb05117835447d72ce08c1d020ec",
+ "contractName": "test",
+ "bytecode": "60606040526040516020806102b183398101604052808051906020019091905050806000819055505061027a806100376000396000f300606060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680632f30c6f61461006757806338cc48311461009e57806362738998146100f357806387cc10e11461011c575b600080fd5b61009c600480803590602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610145565b005b34156100a957600080fd5b6100b1610191565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156100fe57600080fd5b6101066101bb565b6040518082815260200191505060405180910390f35b341561012757600080fd5b61012f6101c4565b6040518082815260200191505060405180910390f35b8160008190555080600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505050565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008054905090565b600073__browser/ballot.sol:testLib____________636d4ce63c6000604051602001526040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160206040518083038186803b151561022e57600080fd5b6102c65a03f4151561023f57600080fd5b505050604051805190509050905600a165627a7a72305820e0b2510bb2890a0334bfe5613d96db3e72442e63b514cdeaee8fc2c6bbd19d3a0029",
+ "linkReferences": {
+ "browser/ballot.sol": {
+ "testLib": [
+ {
+ "length": 20,
+ "start": 511
+ }
+ ]
+ }
+ },
+ "name": "",
+ "type": "constructor",
+ "from": "account{0}"
+ }
+ },
+ {
+ "timestamp": 1512830034180,
+ "record": {
+ "value": "1000000000000000000",
+ "parameters": [
+ 1,
+ "0xca35b7d915458ef540ade6068dfe2f44e8fa733c"
+ ],
+ "to": "created{1512830015080}",
+ "abi": "0xc41589e7559804ea4a2080dad19d876a024ccb05117835447d72ce08c1d020ec",
+ "name": "set",
+ "type": "function",
+ "from": "account{0}"
+ }
+ }
+],
+"abis": {
+ "0xbc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a": [
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "get",
+ "outputs": [
+ {
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ }
+ ],
+ "0xc41589e7559804ea4a2080dad19d876a024ccb05117835447d72ce08c1d020ec": [
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "getInt",
+ "outputs": [
+ {
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "getFromLib",
+ "outputs": [
+ {
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "getAddress",
+ "outputs": [
+ {
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "_t",
+ "type": "uint256"
+ },
+ {
+ "name": "_add",
+ "type": "address"
+ }
+ ],
+ "name": "set",
+ "outputs": [],
+ "payable": true,
+ "stateMutability": "payable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "name": "_r",
+ "type": "uint256"
+ }
+ ],
+ "payable": true,
+ "stateMutability": "payable",
+ "type": "constructor"
+ }
+ ]
+}
+}
+```
diff --git a/docs/settings_tab.md b/docs/settings_tab.md
new file mode 100644
index 0000000000..328b9da44f
--- /dev/null
+++ b/docs/settings_tab.md
@@ -0,0 +1,12 @@
+Settings
+========
+
+This section displays the current compiler version and allows one to change to another version.
+
+
+
+Another important settings:
+
+- Text wrap: controls if the text in the editor should be wrapped.
+
+- Enable optimization: defines if the compiler should enable optimization during compilation. Enabling this option saves execution gas. It is useful to enable optimization for contracts ready to be deployed in production but could lead to some inconsistencies when debugging such a contract.
diff --git a/docs/solidity_editor.md b/docs/solidity_editor.md
new file mode 100644
index 0000000000..df6219afda
--- /dev/null
+++ b/docs/solidity_editor.md
@@ -0,0 +1,18 @@
+Solidity Editor
+===============
+
+The Remix editor recompiles the code each time the current file is
+changed or another file is selected. It also provides syntax
+highlighting mapped to solidity keywords.
+
+
+
+Here's the list of some important features:
+
+- It display opened files as tabs.
+- Compilation Warning and Error are displayed in the gutter
+- Remix saves the current file continuously (5s after the last
+ changes)
+- +/- on the top left corner enable you to increase/decrease the font
+ size of the editor
+
diff --git a/docs/support.md b/docs/support.md
new file mode 100644
index 0000000000..6bdd96968a
--- /dev/null
+++ b/docs/support.md
@@ -0,0 +1,9 @@
+Support chat
+=======================
+
+We know that blockchain ecosystem is very new and that lots of information is scattered around the web.
+That is why we created a community support chat where we and other users try to answer your questions if
+you get stuck using Remix. Please, join [the Remix channel](https://gitter.im/ethereum/remix) and ask community for help.
+
+For anyone who is interested in developing a custom plugin for Remix or who wants to contribute to the codebase,
+we opened [another channel](https://gitter.im/ethereum/remix-dev) specially for developers working on Remix tool.
diff --git a/docs/support_tab.md b/docs/support_tab.md
new file mode 100644
index 0000000000..90ed7a47b2
--- /dev/null
+++ b/docs/support_tab.md
@@ -0,0 +1,9 @@
+Support tab in Remix
+======================
+
+This section provides a link to Remix Issues where users can report a
+bug or suggest a feature, as well as providing other useful links. It
+also displays a [Remix support
+channel](http://gitter.im/ethereum/remix)
+
+
diff --git a/docs/terminal.md b/docs/terminal.md
new file mode 100644
index 0000000000..9c1708996c
--- /dev/null
+++ b/docs/terminal.md
@@ -0,0 +1,20 @@
+Terminal
+========
+
+
+
+Features, available in the terminal:
+
+- It integrates a JavaScript interpreter and the `web3` object. It
+ enables the execution of the JavaScript script which interacts with
+ the current context. (note that `web3` is only available if the
+ `web provider` or `injected provider` mode is selected).
+- It displays important actions made while interacting with the Remix
+ IDE (i.e. sending a new transaction).
+- It displays transactions that are mined in the current context. You
+ can choose to display all transactions or only transactions that
+ refers to the contracts Remix knows (e.g transaction created from
+ the Remix IDE).
+- It allows searching for the data and clearing the logs from the
+ terminal.
+
diff --git a/docs/tuto_basicimport.png b/docs/tuto_basicimport.png
new file mode 100644
index 0000000000..81a8009336
Binary files /dev/null and b/docs/tuto_basicimport.png differ
diff --git a/docs/tuto_importgit.png b/docs/tuto_importgit.png
new file mode 100644
index 0000000000..022eb4b011
Binary files /dev/null and b/docs/tuto_importgit.png differ
diff --git a/docs/tuto_importswarm.png b/docs/tuto_importswarm.png
new file mode 100644
index 0000000000..01fba3e646
Binary files /dev/null and b/docs/tuto_importswarm.png differ
diff --git a/docs/tutorial_debug.md b/docs/tutorial_debug.md
new file mode 100644
index 0000000000..31b33fa96e
--- /dev/null
+++ b/docs/tutorial_debug.md
@@ -0,0 +1,222 @@
+Tutorial on debugging transactions with Remix
+===============================================
+
+The goal of this tutorial is to explain how to debug transaction using
+Remix.
+
+Start debugging
+---------------
+
+There are two different ways to start debugging, each way correspond to
+a different use case.
+
+### From the Transaction GUI
+
+We will not explain in detail here how to write or deploy contract. Let
+us start with a basic contract (replace this one by your's):
+
+``` {.sourceCode .none}
+contract Donation {
+ address owner;
+ event fundMoved(address _to, uint _amount);
+ modifier onlyowner { if (msg.sender == owner) _; }
+ address[] _giver;
+ uint[] _values;
+
+ function Donation() {
+ owner = msg.sender;
+ }
+
+ function donate() payable {
+ addGiver(msg.value);
+ }
+
+ function moveFund(address _to, uint _amount) onlyowner {
+ uint balance = this.balance;
+ uint amount = _amount;
+ if (_amount <= this.balance) {
+ if (_to.send(this.balance)) {
+ fundMoved(_to, _amount);
+ } else {
+ throw;
+ }
+ } else {
+ throw;
+ }
+ }
+
+ function addGiver(uint _amount) internal {
+ _giver.push(msg.sender);
+ _values.push(_amount);
+ }
+}
+```
+
+For the purpose of this tutorial, we will run the `JavaScript VM`
+(that's the default mode when you don't use Remix with Mist or
+Metamask). This simulates a custom blockchain. You could do the same
+using a proper backend node.
+
+Now, let's deploy the contract:
+
+Right panel / Red button `Create`
+
+
+
+Then we should call the `Donate` function (that will send Ether to the
+contract).
+
+Let's set the amount of Ether:
+
+Right panel / second tab from the left - fill in the ´´value´´ input (´1
+ether´ for instance)
+
+
+
+Then click on `Donate`. As we are using the `JavaScript VM`, everything
+goes almost instantly.
+
+Remix displays also some information related to each transaction result.
+In the terminal, the transaction is logged and you can start debugging
+it.
+
+
+
+### From the Debugger
+
+The debugger can be found in the right panel / 5th tab from the left.
+
+You can start a debug session by providing either a `transaction hash`
+or a `block number` and `transaction index`.
+
+
+
+Click the `play` button to start debugging.
+
+Using the debugger
+------------------
+
+The debugger allows one to see detailed informations about the
+transaction's execution. It uses the editor (left panel) to display the
+location in the source code where the current execution is.
+
+The transaction panel displays basic information about the current
+transaction.
+
+
+
+The navigation part contains a slider and buttons that can be used to
+step through the transaction execution.
+
+From the left to the right:
+
+step over back, step into back, step into forward, step over forward,
+jump out (jump out of the current call), jump to the previous
+breakpoint, jump to the next breakpoint.
+
+
+
+11 panels give detailed information about the execution:
+
+### Instructions
+
+
+
+The Instructions panel displays the bytecode of the current executing
+contract- with the current step highlighted.
+
+Important note: When this panel is hidden, the slider will have a
+courser granularity and only stop at expression boundaries, even if they
+are compiled into multiple EVM instructions. When the panel is
+displayed, it will be possible to step over every instruction, even
+those that refers to the same expression.
+
+### Solidity Locals
+
+
+
+The Solidity Locals panel displays local variables associated with the
+current context.
+
+### Solidity State
+
+
+
+The Solidity State panel displays state variables of the current
+executing contract.
+
+### Low level panels
+
+These panels display low level informations about the execution:
+
+> - Stack
+> - Storages Changes
+> - Memory
+> - Call Data
+> - Call Stack
+> - Return Value (only if the current step is a RETURN opcode)
+> - Full Storages Changes (only at the end of the execution - display
+> every storage change of every modified contract)
+
+### Reverted Transaction
+
+A transaction could be reverted (either because of out of gas exception,
+Solidity `throw` or low level exception).
+
+In that case it is important to be aware of the exception and to locate
+where the exception is in the source code.
+
+Remix will warn you when the execution throws an exception. The
+`warning` button will jump to the last opcode before the exception
+happened.
+
+
+
+### Breakpoints
+
+The two last buttons from the navigation area are used to jump either
+back to the previous breakpoint or forward to the next breakpoint.
+
+Breakpoints can be added and removed by clicking on the line number.
+
+
+
+When a debug session is started, the execution will jump to the first
+encountered breakpoint.
+
+Important note: If you add a breakpoint to a line that declares a
+variable, it might be triggered twice: Once for initializing the
+variable to zero and second time for assigning the actual value. As an
+example, assume you are debugging the following contract:
+
+``` {.sourceCode .none}
+contract ctr {
+ function hid () {
+ uint p = 45;
+ uint m;
+ m = 89;
+ uint l = 34;
+ }
+}
+```
+
+And let's says that breakpoints are set for the lines
+
+`uint p = 45;`
+
+`m = 89;`
+
+`uint l = 34;`
+
+then clicking on `Jump to next breakpoint` will stop at the following
+lines in the given order:
+
+> `uint p = 45;` (declaration of p)
+>
+> `uint l = 34;` (declaration of l)
+>
+> `uint p = 45;` (45 assigned to p)
+>
+> `m = 89;` (89 assigned to m)
+>
+> `uint l = 34;` (34 assigned to l)
diff --git a/docs/tutorial_import.md b/docs/tutorial_import.md
new file mode 100644
index 0000000000..18fc416030
--- /dev/null
+++ b/docs/tutorial_import.md
@@ -0,0 +1,34 @@
+Importing Source Files in Solidity
+==================================
+
+This tutorial will show you how to import local and external files.
+
+The compilation result will also contain contracts implemented in the
+imported files.
+
+For a detailed explanation of the `import` keyword see the
+[Solidity documentation](https://solidity.readthedocs.io/en/develop/layout-of-source-files.html?highlight=import#importing-other-source-files)
+
+Importing a local file
+----------------------
+
+Other files in Remix can be imported just by specifying their path.
+Please use ./ for relative paths to increase portability.
+
+
+
+Importing from Github
+---------------------
+
+It is possible to import files directly from github with URLs like
+`https://github.com///`.
+
+
+
+Importing from Swarm
+--------------------
+
+Files can be imported using all URLs supported by swarm. If you do not
+have a swarm node, swarm-gateways.net will be used instead.
+
+
diff --git a/docs/tutorial_mist.md b/docs/tutorial_mist.md
new file mode 100644
index 0000000000..568cf34b46
--- /dev/null
+++ b/docs/tutorial_mist.md
@@ -0,0 +1,309 @@
+Debugging a Dapp using Remix - Mist - Geth
+==========================================
+
+The ultimate goal of this tutorial is to debug transactions that have
+been created by a dapp front end.
+
+It is easy in Remix to debug a transaction created from its own GUI.
+However, setting up an environment that allows you to debug transactions
+created outside of Remix, require a bit more of complexity.
+
+We will need four tools for that :
+
+> - Geth - this is the center piece and provides the blockchain
+> environment. We will basically run geth in a dev mode.
+> - Mist - this is the Ethereum dapp browser. We will use it to browse
+> our front end.
+> - Remix - this is the Ethereum IDE. We will use it to develop our
+> Solidity contract.
+> - Any code editor you want - in order to write your front end :)
+
+Install the environment
+-----------------------
+
+### Install Mist
+
+Mist is the Ethereum browser and the entry point of a Dapp.
+
+Please download [the latest
+version](http://github.com/ethereum/mist/releases) (at least 0.8.9).
+
+Basically we will always run our front end in Mist (note that it is also
+possible to use [Metamask](http://metamask.io)).
+
+### Install Geth
+
+[Geth](http://github.com/ethereum/go-ethereum/releases) is the official
+Ethereum client.
+
+Running the environment
+-----------------------
+
+### Run Geth
+
+We will run a test node. This node will have a new empty state and will
+not be synced to the main or ropsten network.
+
+ geth --ipcpath /geth.ipc --datadir --dev console
+
+`` is the folder where keys and chain data will be
+stored.
+
+`--ipcpath` defines the end point that other apps (like Mist) use to
+talk to geth.
+
+`--datadir` specifies the data directory.
+
+`--dev` sets the node into private chain mode and adds some debugging
+flags.
+
+Then we need to create accounts and mine a bit to generate some Ether:
+
+ // from the geth console :
+ personal.newAccount() // You can execute this command several time if you need more than one account.
+ miner.start() // generate some Ether.
+ miner.stop() // stop mining after 30s-60s - we could also keep mining.
+
+Next time we run Geth, we will only need to mine transactions (no need
+to recreate account).
+
+### Run Mist
+
+If we run Mist without any argument, its internal Geth node will run. As
+we have our own we need to specify the ipc path of the node installed
+above.
+
+ mist --rpc /geth.ipc
+
+(yes the option is --rpc)
+
+Once Mist is started, verify that it is connected to the test node
+(that's very important!!).
+
+On the bottom left, check that the network is `Private-net` and that the
+block number is the same as reported by the test node we are currently
+running. Run the following command in the Geth Console to check:
+web3.eth.blockNumber.
+
+
+
+Clicking on Wallet will allow you to send transactions and check account
+balances (if you are currently mining you should see the balance
+increasing).
+
+### Starting Remix
+
+In Mist click on `Develop` / `Open Remix IDE`
+
+Remix will open in a new window. If this is the first time it is run,
+the `Ballot` contract will be loaded.
+
+Now, we need to check if Remix is connected to Mist:
+
+Right panel / third tab from the left, `Injected Provider` should be
+checked.
+
+
+
+Right panel / second tab from the left, `Transaction Origin` should
+contain accounts we have previously created in Geth.
+
+
+
+Developing contract / front end
+-------------------------------
+
+### Donation contract - Dapp Back end
+
+Here is a sample solidity contract.
+
+Copy and paste the following inside remix:
+
+``` {.sourceCode .none}
+contract Donation {
+ address owner;
+ event fundMoved(address _to, uint _amount);
+ modifier onlyowner { if (msg.sender == owner) _; }
+ address[] _giver;
+ uint[] _values;
+
+ function Donation() {
+ owner = msg.sender;
+ }
+
+ function donate() payable {
+ addGiver(msg.value);
+ }
+
+ function moveFund(address _to, uint _amount) onlyowner {
+ uint balance = this.balance;
+ uint amount = _amount;
+ if (_amount <= this.balance) {
+ if (_to.send(this.balance)) {
+ fundMoved(_to, _amount);
+ } else {
+ throw;
+ }
+ } else {
+ throw;
+ }
+ }
+
+ function addGiver(uint _amount) internal {
+ _giver.push(msg.sender);
+ _values.push(_amount);
+ }
+}
+```
+
+### Dapp Front end
+
+and here is the front end:
+
+``` {.sourceCode .none}
+
+
Donation Contract
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+I would suggest serving this file using `http-serve`, but you can use
+any web server you like.
+
+Example: Dapp Front End
+
+### Important notice !
+
+The variable `contractspec` contains the abi of the `donation` contract.
+This means that if you change something in the contract interface
+(function names, parameters, ...) you need to copy the new abi from
+remix to the front end.
+
+Deploying
+---------
+
+Right panel / Red button `Create`
+
+
+
+This creates a new transaction that deploys the `Donation` contract
+(Mist will ask for the usual passphrase check).
+
+Wait for the transaction to be mined (don't forget to activate mining
+`miner.start()`). Once this is done, you can use it by executing the
+`moveFund` and `donate` function. But this is not what we want to
+achieve. We want to run and debug those functions from the front end.
+
+Remix also display the address of the contract. Save it, we'll need this
+address later.
+
+
+
+Debugging
+---------
+
+From Mist, browse the above front end. In the first field, paste the
+address of the newly created contract. Now, let's call the first
+function (label `give`).
+
+You will need an account and a value.
+
+The account could be any account that is declared in the Wallet section
+of Mist. This is the sender of the transaction that we are going to
+create. The value should be no more than the actual balance of the
+account - the unit is in wei, so just put `100` (100 wei), that should
+be fine.
+
+Click on `Give` and wait for the transaction to be mined.
+
+The HTML block with id `log` is filled by all the transactions created
+from the front end. It was easier for the purpose of this tutorial to
+just log transactions in a div but you can have your own logging
+mechanism.
+
+There is only one field that we need, this is the `transactionHash`.
+
+Copy it and switch to Remix. On the right side, the fifth panel shows a
+small "bug" icon, that is the debugger.
+
+Paste the hash into the transaction field and click on the `play`
+button.
+
+
+
+You are now entering a debug session for the call to `donate`.
+
+Debugging in Remix is easier than with common tools like gdb because you
+can freely move in time. Use the slider to change the current step and
+click on the panels below to expand them and explore the curret state,
+local variables, etc. There are also breakpoints to move between
+sections of the code quickly, but more on all that later.
+
+At the time of this writing, there is an issue that could break the
+contract creation. The a workaround for that at
+ . Please follow
+the workaround or wait for this issue to be closed.
+
+Also, although retrieving a contract's storage when Remix is using the
+JavaScript VM is working well, there is still work to be done when Remix
+is using eth or geth as backend.
diff --git a/docs/tutorial_remixd_filesystem.md b/docs/tutorial_remixd_filesystem.md
new file mode 100644
index 0000000000..1acdc88923
--- /dev/null
+++ b/docs/tutorial_remixd_filesystem.md
@@ -0,0 +1,45 @@
+Access your local filesystem by using RemixD
+===================================================
+
+RemixD is an npm module. Its purpose is to give the remix web
+application access to a folder from your local computer.
+
+The code of RemixD can be checked out
+[here](https://github.com/ethereum/remixd) .
+
+Remixd can be globally installed using the following command:
+`npm install -g remixd`.
+
+Then `remixd -s ` will start Remixd
+and share the given folder.
+
+The folder is shared using a websocket connection between `Remix IDE`
+and `Remixd`.
+
+Be sure the user executing Remix has read/write permission on the
+folder.
+
+**Warning!**
+
+RemixD provides `full read and write access` to the given folder for `any
+application` that can access the `TCP port 65520` on your local host.
+
+From `Remix IDE`, you will need to activate the connection.
+
+Click on the `localhost connection` icon:
+
+
+
+A modal dialog will ask confirmation
+
+
+
+Accepting this dialog will start a session. Once the connection is made,
+the status will update and the connection icon should shows up in green.
+
+Hovering the icon will give more connection status information.
+
+At this point if the connection is successful, the shared folder will be
+available in the file explorer.
+
+
diff --git a/docs/udapp.md b/docs/udapp.md
new file mode 100644
index 0000000000..642c0e2012
--- /dev/null
+++ b/docs/udapp.md
@@ -0,0 +1,25 @@
+Deployed contracts
+====================
+
+This section in the Run tab contains a list of deployed contracts to interact with through autogenerated UI of the deployed contract (also called udapp).
+
+Several cases apply:
+
+- The called function is declared as `constant` or `pure` in Solidity. The action has a blue background, clicking it does not
+create a new transaction. Clicking it is not necessary because there are not state changes - but it will update the return
+value of the function.
+
+- The called function has no special keywords. The action has a
+light red background, clicking on does create a new transaction.
+But this transaction cannot accept any amount of Ether.
+
+- The called function is declared as `payable` in Solidity. The
+action has a red background, clicking it does create a new
+transaction and this transaction can accept value.
+
+
+For more information see more about [Solidity
+modifier](http://solidity.readthedocs.io/en/develop/miscellaneous.html?highlight=pure#modifiers)
+.
+
+If a function requires input parameters, it is required to specify them.
diff --git a/docs/workshop_Building_smart_contracts_with_Remix.md b/docs/workshop_Building_smart_contracts_with_Remix.md
new file mode 100644
index 0000000000..25ab50d5fb
--- /dev/null
+++ b/docs/workshop_Building_smart_contracts_with_Remix.md
@@ -0,0 +1,8 @@
+Building Smart Contracts with Remix
+=======================
+
+We prepared a thorough workshop that will help you build your own DApp. We will show you how to build and deploy smart contracts, how to deploy them and how to interact with them. Later in the workshop you will also learn how to connect your frontend with blockchain by using web3.js.
+
+### Let's get started
+
+This workshop was part of preparations for ethCC and Edcon. You can [watch the presentation talk](https://www.youtube.com/watch?v=nAI_Cr5Y8JY) and in parallel open the [workshop slides](https://slides.com/ninabreznik/deck-11-13#/) and follow along.
diff --git a/docs_old/Makefile b/docs_old/Makefile
new file mode 100644
index 0000000000..a04587262d
--- /dev/null
+++ b/docs_old/Makefile
@@ -0,0 +1,216 @@
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS =
+SPHINXBUILD = sphinx-build
+PAPER =
+BUILDDIR = _build
+
+# User-friendly check for sphinx-build
+ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
+$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
+endif
+
+# Internal variables.
+PAPEROPT_a4 = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+# the i18n builder cannot share the environment and doctrees with the others
+I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+
+.PHONY: help
+help:
+ @echo "Please use \`make ' where is one of"
+ @echo " html to make standalone HTML files"
+ @echo " dirhtml to make HTML files named index.html in directories"
+ @echo " singlehtml to make a single large HTML file"
+ @echo " pickle to make pickle files"
+ @echo " json to make JSON files"
+ @echo " htmlhelp to make HTML files and a HTML help project"
+ @echo " qthelp to make HTML files and a qthelp project"
+ @echo " applehelp to make an Apple Help Book"
+ @echo " devhelp to make HTML files and a Devhelp project"
+ @echo " epub to make an epub"
+ @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+ @echo " latexpdf to make LaTeX files and run them through pdflatex"
+ @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
+ @echo " text to make text files"
+ @echo " man to make manual pages"
+ @echo " texinfo to make Texinfo files"
+ @echo " info to make Texinfo files and run them through makeinfo"
+ @echo " gettext to make PO message catalogs"
+ @echo " changes to make an overview of all changed/added/deprecated items"
+ @echo " xml to make Docutils-native XML files"
+ @echo " pseudoxml to make pseudoxml-XML files for display purposes"
+ @echo " linkcheck to check all external links for integrity"
+ @echo " doctest to run all doctests embedded in the documentation (if enabled)"
+ @echo " coverage to run coverage check of the documentation (if enabled)"
+
+.PHONY: clean
+clean:
+ rm -rf $(BUILDDIR)/*
+
+.PHONY: html
+html:
+ $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+.PHONY: dirhtml
+dirhtml:
+ $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+.PHONY: singlehtml
+singlehtml:
+ $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
+ @echo
+ @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
+
+.PHONY: pickle
+pickle:
+ $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+ @echo
+ @echo "Build finished; now you can process the pickle files."
+
+.PHONY: json
+json:
+ $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+ @echo
+ @echo "Build finished; now you can process the JSON files."
+
+.PHONY: htmlhelp
+htmlhelp:
+ $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+ @echo
+ @echo "Build finished; now you can run HTML Help Workshop with the" \
+ ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+.PHONY: qthelp
+qthelp:
+ $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+ @echo
+ @echo "Build finished; now you can run "qcollectiongenerator" with the" \
+ ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+ @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Remix.qhcp"
+ @echo "To view the help file:"
+ @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Remix.qhc"
+
+.PHONY: applehelp
+applehelp:
+ $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp
+ @echo
+ @echo "Build finished. The help book is in $(BUILDDIR)/applehelp."
+ @echo "N.B. You won't be able to view it unless you put it in" \
+ "~/Library/Documentation/Help or install it in your application" \
+ "bundle."
+
+.PHONY: devhelp
+devhelp:
+ $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
+ @echo
+ @echo "Build finished."
+ @echo "To view the help file:"
+ @echo "# mkdir -p $$HOME/.local/share/devhelp/Remix"
+ @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Remix"
+ @echo "# devhelp"
+
+.PHONY: epub
+epub:
+ $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
+ @echo
+ @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
+
+.PHONY: latex
+latex:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo
+ @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+ @echo "Run \`make' in that directory to run these through (pdf)latex" \
+ "(use \`make latexpdf' here to do that automatically)."
+
+.PHONY: latexpdf
+latexpdf:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo "Running LaTeX files through pdflatex..."
+ $(MAKE) -C $(BUILDDIR)/latex all-pdf
+ @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+.PHONY: latexpdfja
+latexpdfja:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo "Running LaTeX files through platex and dvipdfmx..."
+ $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
+ @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+.PHONY: text
+text:
+ $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
+ @echo
+ @echo "Build finished. The text files are in $(BUILDDIR)/text."
+
+.PHONY: man
+man:
+ $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
+ @echo
+ @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
+
+.PHONY: texinfo
+texinfo:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo
+ @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
+ @echo "Run \`make' in that directory to run these through makeinfo" \
+ "(use \`make info' here to do that automatically)."
+
+.PHONY: info
+info:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo "Running Texinfo files through makeinfo..."
+ make -C $(BUILDDIR)/texinfo info
+ @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
+
+.PHONY: gettext
+gettext:
+ $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
+ @echo
+ @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
+
+.PHONY: changes
+changes:
+ $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+ @echo
+ @echo "The overview file is in $(BUILDDIR)/changes."
+
+.PHONY: linkcheck
+linkcheck:
+ $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+ @echo
+ @echo "Link check complete; look for any errors in the above output " \
+ "or in $(BUILDDIR)/linkcheck/output.txt."
+
+.PHONY: doctest
+doctest:
+ $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+ @echo "Testing of doctests in the sources finished, look at the " \
+ "results in $(BUILDDIR)/doctest/output.txt."
+
+.PHONY: coverage
+coverage:
+ $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage
+ @echo "Testing of coverage in the sources finished, look at the " \
+ "results in $(BUILDDIR)/coverage/python.txt."
+
+.PHONY: xml
+xml:
+ $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
+ @echo
+ @echo "Build finished. The XML files are in $(BUILDDIR)/xml."
+
+.PHONY: pseudoxml
+pseudoxml:
+ $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
+ @echo
+ @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
diff --git a/docs_old/analysis_tab.rst b/docs_old/analysis_tab.rst
new file mode 100644
index 0000000000..22f34b19f3
--- /dev/null
+++ b/docs_old/analysis_tab.rst
@@ -0,0 +1,27 @@
+Analysis
+========
+
+This section gives information about the last compilation.
+By default, a new analysis is run at each compilation.
+
+The analysis tab gives detailed information about the contract code. It can help you avoid code mistakes and to enforce best practices.
+
+.. image:: images/remix_analysistab.png
+
+Here is the list of analyzers:
+
+ - Security:
+ - Transaction origin: Warns if tx.origin is used
+ - Check effects: Avoid potential reentrancy bugs
+ - Inline assembly: Use of Inline Assembly
+ - Block timestamp: Semantics maybe unclear
+ - Low level calls: Semantics maybe unclear
+ - Block.blockhash usage: Semantics maybe unclear
+
+ - Gas & Economy:
+ - Gas costs: Warns if the gas requirements of the functions are too high
+ - This on local calls: Invocation of local functions via this
+
+ - Miscellaneous:
+ - Constant functions: Checks for potentially constant functions
+ - Similar variable names: Checks if variable names are too similar
diff --git a/docs_old/compile_tab.rst b/docs_old/compile_tab.rst
new file mode 100644
index 0000000000..47b376b095
--- /dev/null
+++ b/docs_old/compile_tab.rst
@@ -0,0 +1,20 @@
+Compiling contracts
+===================
+
+By default Remix triggers a compilation each time the current file is changed or another file is selected.
+If the contract has a lot of dependencies and takes a long time to compile, it is possible to disable the `autocompilation`.
+
+.. image:: images/remix_compiletab.png
+
+After each compilation, a list is updated with all the newly compiled contracts.
+
+`Details` modal dialog displays detailed information about the current selected contract.
+
+From this tab, you can also publish your contract to Swarm (only non abstract contracts can be published).
+
+Published data notably contains the ``abi`` and solidity source code.
+
+After a contract is published, you can find its metadata information using the `bzz` URL located in the details modal dialog ``SWARM LOCATION``.
+
+Compilation Errors and Warning are displayed below the contract section. At each compilation, the static analysis tab builds a report. It is very valuable when addressing reported issues even if the compiler doesn't complain.
+(see :doc:`../analysis_tab`)
diff --git a/docs_old/conf.py b/docs_old/conf.py
new file mode 100644
index 0000000000..8df0904e6c
--- /dev/null
+++ b/docs_old/conf.py
@@ -0,0 +1,283 @@
+# -*- coding: utf-8 -*-
+#
+# Remix documentation build configuration file, created by
+# sphinx-quickstart on Mon Feb 20 12:16:16 2017.
+#
+# This file is execfile()d with the current directory set to its
+# containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys
+import os
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#sys.path.insert(0, os.path.abspath('.'))
+
+# -- General configuration ------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = []
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix(es) of source filenames.
+# You can specify multiple suffix as a list of string:
+# source_suffix = ['.rst', '.md']
+source_suffix = '.md'
+
+# The encoding of source files.
+#source_encoding = 'utf-8-sig'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'Remix'
+copyright = u'2017, yann300'
+author = u'yann300'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = u'1'
+# The full version, including alpha/beta/rc tags.
+release = u'1'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#
+# This is also used if you do content translation via gettext catalogs.
+# Usually you set "language" from the command line for these cases.
+language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+exclude_patterns = ['_build']
+
+# The reST default role (used for this markup: `text`) to use for all
+# documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+# If true, keep warnings as "system message" paragraphs in the built documents.
+#keep_warnings = False
+
+# If true, `todo` and `todoList` produce output, else they produce nothing.
+todo_include_todos = False
+
+
+# -- Options for HTML output ----------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+html_theme = 'alabaster'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further. For a list of options available for each theme, see the
+# documentation.
+#html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+#html_theme_path = []
+
+# The name for this set of Sphinx documents. If None, it defaults to
+# " v documentation".
+#html_title = None
+
+# A shorter title for the navigation bar. Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (relative to this directory) to use as a favicon of
+# the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# Add any extra paths that contain custom files (such as robots.txt or
+# .htaccess) here, relative to this directory. These files are copied
+# directly to the root of the documentation.
+#html_extra_path = []
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_domain_indices = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#html_show_sourcelink = True
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+#html_show_sphinx = True
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+#html_show_copyright = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a tag referring to it. The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = None
+
+# Language to be used for generating the HTML full-text search index.
+# Sphinx supports the following languages:
+# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja'
+# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr'
+#html_search_language = 'en'
+
+# A dictionary with options for the search language support, empty by default.
+# Now only 'ja' uses this config value
+#html_search_options = {'type': 'default'}
+
+# The name of a javascript file (relative to the configuration directory) that
+# implements a search results scorer. If empty, the default will be used.
+#html_search_scorer = 'scorer.js'
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'Remixdoc'
+
+# -- Options for LaTeX output ---------------------------------------------
+
+latex_elements = {
+# The paper size ('letterpaper' or 'a4paper').
+#'papersize': 'letterpaper',
+
+# The font size ('10pt', '11pt' or '12pt').
+#'pointsize': '10pt',
+
+# Additional stuff for the LaTeX preamble.
+#'preamble': '',
+
+# Latex figure (float) alignment
+#'figure_align': 'htbp',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title,
+# author, documentclass [howto, manual, or own class]).
+latex_documents = [
+ (master_doc, 'Remix.tex', u'Remix Documentation',
+ u'yann300', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# If true, show page references after internal links.
+#latex_show_pagerefs = False
+
+# If true, show URL addresses after external links.
+#latex_show_urls = False
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_domain_indices = True
+
+
+# -- Options for manual page output ---------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+ (master_doc, 'remix', u'Remix Documentation',
+ [author], 1)
+]
+
+# If true, show URL addresses after external links.
+#man_show_urls = False
+
+
+# -- Options for Texinfo output -------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+# dir menu entry, description, category)
+texinfo_documents = [
+ (master_doc, 'Remix', u'Remix Documentation',
+ author, 'Remix', 'One line description of project.',
+ 'Miscellaneous'),
+]
+
+# Documents to append as an appendix to all manuals.
+#texinfo_appendices = []
+
+# If false, no module index is generated.
+#texinfo_domain_indices = True
+
+# How to display URL addresses: 'footnote', 'no', or 'inline'.
+#texinfo_show_urls = 'footnote'
+
+# If true, do not generate a @detailmenu in the "Top" node's menu.
+#texinfo_no_detailmenu = False
diff --git a/docs_old/debugger_tab.rst b/docs_old/debugger_tab.rst
new file mode 100644
index 0000000000..cb73c90ca8
--- /dev/null
+++ b/docs_old/debugger_tab.rst
@@ -0,0 +1,11 @@
+Debugging
+=========
+
+This module allows you to debug the transaction.
+It can be used to deploy transactions created from Remix and already mined transactions.
+(debugging works only if the current environment provides the necessary features).
+
+.. image:: images/remix_debuggertab.png
+
+For more information about debugging, click on the following link:
+:doc:`../tutorial_debug`
diff --git a/docs_old/file_explorer.rst b/docs_old/file_explorer.rst
new file mode 100644
index 0000000000..e77ec2976a
--- /dev/null
+++ b/docs_old/file_explorer.rst
@@ -0,0 +1,37 @@
+File Explorer
+=============
+
+The file explorer lists by default all the files stored in your browser. You can see them in the `browser` folder. You can always rename, remove or add new files to the file explorer.
+
+.. image:: images/remix_file_explorer_browser.png
+
+Note that clearing the browser storage will permanently delete all the solidity files you wrote. To avoid this, you can use Remixd, which enables you to store and sync files in the browser with your local computer (for more information see :doc:`../tutorial_remixd_filesystem`)
+
+.. image:: images/remix_file_explorer_menu.png
+
+We will start by reviewing at the icons at the top left - from left to the right:
+
+Create new File
+---------------
+
+Creates a new file in the `browser` explorer.
+
+Add Local File
+--------------
+
+Allows you to select files from the local file system and import them to the Remix browser storage.
+
+Publish to Gist
+---------------
+
+Publishes files from the browser storage to an anonymous public gist.
+
+Copy to another instance
+------------------------
+
+Enables you to copy files from the browser storage to another instance (URL) of Remix.
+
+Connect to Localhost
+--------------------
+
+Allows to use file located in your file system (see :doc:`../tutorial_remixd_filesystem`).
diff --git a/docs_old/images/remix_analysistab.png b/docs_old/images/remix_analysistab.png
new file mode 100755
index 0000000000..e43143962c
Binary files /dev/null and b/docs_old/images/remix_analysistab.png differ
diff --git a/docs_old/images/remix_compiletab.png b/docs_old/images/remix_compiletab.png
new file mode 100755
index 0000000000..cd51b11574
Binary files /dev/null and b/docs_old/images/remix_compiletab.png differ
diff --git a/docs_old/images/remix_debuggertab.png b/docs_old/images/remix_debuggertab.png
new file mode 100755
index 0000000000..104d9744b2
Binary files /dev/null and b/docs_old/images/remix_debuggertab.png differ
diff --git a/docs_old/images/remix_editor.png b/docs_old/images/remix_editor.png
new file mode 100755
index 0000000000..9260f05fe4
Binary files /dev/null and b/docs_old/images/remix_editor.png differ
diff --git a/docs_old/images/remix_file_explorer_browser.png b/docs_old/images/remix_file_explorer_browser.png
new file mode 100755
index 0000000000..3f09c0c752
Binary files /dev/null and b/docs_old/images/remix_file_explorer_browser.png differ
diff --git a/docs_old/images/remix_file_explorer_menu.png b/docs_old/images/remix_file_explorer_menu.png
new file mode 100755
index 0000000000..2140e9b36c
Binary files /dev/null and b/docs_old/images/remix_file_explorer_menu.png differ
diff --git a/docs_old/images/remix_quickstart_javascriptvm_callinginstance.png b/docs_old/images/remix_quickstart_javascriptvm_callinginstance.png
new file mode 100644
index 0000000000..fe11a2cb06
Binary files /dev/null and b/docs_old/images/remix_quickstart_javascriptvm_callinginstance.png differ
diff --git a/docs_old/images/remix_quickstart_javascriptvm_creation.png b/docs_old/images/remix_quickstart_javascriptvm_creation.png
new file mode 100644
index 0000000000..fc7598aea7
Binary files /dev/null and b/docs_old/images/remix_quickstart_javascriptvm_creation.png differ
diff --git a/docs_old/images/remix_quickstart_javascriptvm_creationTransaction.png b/docs_old/images/remix_quickstart_javascriptvm_creationTransaction.png
new file mode 100644
index 0000000000..0b27bc105f
Binary files /dev/null and b/docs_old/images/remix_quickstart_javascriptvm_creationTransaction.png differ
diff --git a/docs_old/images/remix_recorder.png b/docs_old/images/remix_recorder.png
new file mode 100644
index 0000000000..1f7abc7363
Binary files /dev/null and b/docs_old/images/remix_recorder.png differ
diff --git a/docs_old/images/remix_runtab.png b/docs_old/images/remix_runtab.png
new file mode 100755
index 0000000000..c636298493
Binary files /dev/null and b/docs_old/images/remix_runtab.png differ
diff --git a/docs_old/images/remix_runtab_example.png b/docs_old/images/remix_runtab_example.png
new file mode 100755
index 0000000000..78fba7e994
Binary files /dev/null and b/docs_old/images/remix_runtab_example.png differ
diff --git a/docs_old/images/remix_settingstab.png b/docs_old/images/remix_settingstab.png
new file mode 100755
index 0000000000..bfd50332e1
Binary files /dev/null and b/docs_old/images/remix_settingstab.png differ
diff --git a/docs_old/images/remix_supporttab.png b/docs_old/images/remix_supporttab.png
new file mode 100755
index 0000000000..b62ead496b
Binary files /dev/null and b/docs_old/images/remix_supporttab.png differ
diff --git a/docs_old/images/remix_terminal.png b/docs_old/images/remix_terminal.png
new file mode 100755
index 0000000000..3f096a2490
Binary files /dev/null and b/docs_old/images/remix_terminal.png differ
diff --git a/docs_old/index.rst b/docs_old/index.rst
new file mode 100644
index 0000000000..5534f9ca33
--- /dev/null
+++ b/docs_old/index.rst
@@ -0,0 +1,60 @@
+Remix - Solidity IDE
+====================
+
+Remix is an IDE for the smart contract programming language Solidity and has
+an integrated debugger and testing environment.
+
+An up to date online version is available at `remix.ethereum.org `_
+
+This page will host documentation and tutorial about features Remix provides.
+
+Please go to `solidity.readthedocs.io `_ for any information regarding ``Solidity``
+
+Overview
+--------
+
+Remix provides an integrated development environment (IDE) for smart contract development.
+It focuses on the development and deployment of Solidity written smart contracts.
+
+Remix is a good solution if you intend to:
+
+ - Develop smart contracts (remix integrates a solidity editor).
+ - Debug a smart contract's execution.
+ - Access the state and properties of already deployed smart contract.
+ - Debug already committed transaction.
+ - Analyze solidity code to reduce coding mistakes and to enforce best practices.
+ - Together with Mist (or any tool which inject web3), Remix can be used to test and debug a Dapp (see :doc:`../tutorial_mist`)
+
+Developing smart contract requires a deep understanding of the associated Blockchain technology.
+
+!! Don't use Remix against a production network unless you are completely sure what you are doing !!
+
+This documentation describes all the features remix provides.
+The GUI can be separated in 4 parts. Click on one the link to get more information.
+
+ - :doc:`../file_explorer`
+ - :doc:`../solidity_editor`
+ - :doc:`../terminal`
+ - :doc:`../tabs_panel`
+ - :doc:`../compile_tab`
+ - :doc:`../run_tab`
+ - :doc:`../settings_tab`
+ - :doc:`../debugger_tab`
+ - :doc:`../analysis_tab`
+ - :doc:`../support_tab`
+
+Quick Start
+-----------
+
+(see :doc:`../quickstart_javascriptvm`)
+
+Tutorial
+--------
+
+.. toctree::
+ :maxdepth: 1
+
+ tutorial_remixd_filesystem.rst
+ tutorial_mist.rst
+ tutorial_debug.rst
+ tutorial_import.rst
diff --git a/docs_old/make.bat b/docs_old/make.bat
new file mode 100755
index 0000000000..963594cac0
--- /dev/null
+++ b/docs_old/make.bat
@@ -0,0 +1,263 @@
+@ECHO OFF
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+ set SPHINXBUILD=sphinx-build
+)
+set BUILDDIR=_build
+set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
+set I18NSPHINXOPTS=%SPHINXOPTS% .
+if NOT "%PAPER%" == "" (
+ set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
+ set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
+)
+
+if "%1" == "" goto help
+
+if "%1" == "help" (
+ :help
+ echo.Please use `make ^` where ^ is one of
+ echo. html to make standalone HTML files
+ echo. dirhtml to make HTML files named index.html in directories
+ echo. singlehtml to make a single large HTML file
+ echo. pickle to make pickle files
+ echo. json to make JSON files
+ echo. htmlhelp to make HTML files and a HTML help project
+ echo. qthelp to make HTML files and a qthelp project
+ echo. devhelp to make HTML files and a Devhelp project
+ echo. epub to make an epub
+ echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
+ echo. text to make text files
+ echo. man to make manual pages
+ echo. texinfo to make Texinfo files
+ echo. gettext to make PO message catalogs
+ echo. changes to make an overview over all changed/added/deprecated items
+ echo. xml to make Docutils-native XML files
+ echo. pseudoxml to make pseudoxml-XML files for display purposes
+ echo. linkcheck to check all external links for integrity
+ echo. doctest to run all doctests embedded in the documentation if enabled
+ echo. coverage to run coverage check of the documentation if enabled
+ goto end
+)
+
+if "%1" == "clean" (
+ for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
+ del /q /s %BUILDDIR%\*
+ goto end
+)
+
+
+REM Check if sphinx-build is available and fallback to Python version if any
+%SPHINXBUILD% 1>NUL 2>NUL
+if errorlevel 9009 goto sphinx_python
+goto sphinx_ok
+
+:sphinx_python
+
+set SPHINXBUILD=python -m sphinx.__init__
+%SPHINXBUILD% 2> nul
+if errorlevel 9009 (
+ echo.
+ echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
+ echo.installed, then set the SPHINXBUILD environment variable to point
+ echo.to the full path of the 'sphinx-build' executable. Alternatively you
+ echo.may add the Sphinx directory to PATH.
+ echo.
+ echo.If you don't have Sphinx installed, grab it from
+ echo.http://sphinx-doc.org/
+ exit /b 1
+)
+
+:sphinx_ok
+
+
+if "%1" == "html" (
+ %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/html.
+ goto end
+)
+
+if "%1" == "dirhtml" (
+ %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
+ goto end
+)
+
+if "%1" == "singlehtml" (
+ %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
+ goto end
+)
+
+if "%1" == "pickle" (
+ %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can process the pickle files.
+ goto end
+)
+
+if "%1" == "json" (
+ %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can process the JSON files.
+ goto end
+)
+
+if "%1" == "htmlhelp" (
+ %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can run HTML Help Workshop with the ^
+.hhp project file in %BUILDDIR%/htmlhelp.
+ goto end
+)
+
+if "%1" == "qthelp" (
+ %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can run "qcollectiongenerator" with the ^
+.qhcp project file in %BUILDDIR%/qthelp, like this:
+ echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Remix.qhcp
+ echo.To view the help file:
+ echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Remix.ghc
+ goto end
+)
+
+if "%1" == "devhelp" (
+ %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished.
+ goto end
+)
+
+if "%1" == "epub" (
+ %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The epub file is in %BUILDDIR%/epub.
+ goto end
+)
+
+if "%1" == "latex" (
+ %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
+ goto end
+)
+
+if "%1" == "latexpdf" (
+ %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+ cd %BUILDDIR%/latex
+ make all-pdf
+ cd %~dp0
+ echo.
+ echo.Build finished; the PDF files are in %BUILDDIR%/latex.
+ goto end
+)
+
+if "%1" == "latexpdfja" (
+ %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+ cd %BUILDDIR%/latex
+ make all-pdf-ja
+ cd %~dp0
+ echo.
+ echo.Build finished; the PDF files are in %BUILDDIR%/latex.
+ goto end
+)
+
+if "%1" == "text" (
+ %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The text files are in %BUILDDIR%/text.
+ goto end
+)
+
+if "%1" == "man" (
+ %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The manual pages are in %BUILDDIR%/man.
+ goto end
+)
+
+if "%1" == "texinfo" (
+ %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
+ goto end
+)
+
+if "%1" == "gettext" (
+ %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
+ goto end
+)
+
+if "%1" == "changes" (
+ %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.The overview file is in %BUILDDIR%/changes.
+ goto end
+)
+
+if "%1" == "linkcheck" (
+ %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Link check complete; look for any errors in the above output ^
+or in %BUILDDIR%/linkcheck/output.txt.
+ goto end
+)
+
+if "%1" == "doctest" (
+ %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Testing of doctests in the sources finished, look at the ^
+results in %BUILDDIR%/doctest/output.txt.
+ goto end
+)
+
+if "%1" == "coverage" (
+ %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Testing of coverage in the sources finished, look at the ^
+results in %BUILDDIR%/coverage/python.txt.
+ goto end
+)
+
+if "%1" == "xml" (
+ %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The XML files are in %BUILDDIR%/xml.
+ goto end
+)
+
+if "%1" == "pseudoxml" (
+ %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.
+ goto end
+)
+
+:end
diff --git a/docs_old/mist1.png b/docs_old/mist1.png
new file mode 100644
index 0000000000..cd2991e2fd
Binary files /dev/null and b/docs_old/mist1.png differ
diff --git a/docs_old/quickstart_javascriptvm.rst b/docs_old/quickstart_javascriptvm.rst
new file mode 100644
index 0000000000..4caff3e1c2
--- /dev/null
+++ b/docs_old/quickstart_javascriptvm.rst
@@ -0,0 +1,86 @@
+Quick Start using the JavaScript VM
+===================================
+
+There are 3 type of environments Remix can be plugged to: ``Javascript VM``, ``Injected provider``, or ``Web3 provider``. (see :doc:`../run_tab`)
+
+Both ``Web3 provider`` and ``Injected provider`` require the use of an external tool.
+
+The external tool for ``Web3 provider`` is an Ethereum node the tools for ``Injected provider`` are Mist or Metamask.
+
+The ``JavaScript VM`` mode is convenient because each execution runs in your browser.
+Thus reloading the page will restart Remix with an empty state.
+
+So for performance purposes, it might also be better to use an external node.
+
+Selecting the VM mode
+---------------------
+
+Make sure the VM mode is selected. All accounts displayed in ``Accounts`` should have 100 ether.
+
+Sample contract
+---------------
+
+.. code-block:: none
+
+ pragma solidity ^0.4.16;
+ contract testContract {
+
+ uint value;
+ function testContract(uint _p) {
+ value = _p;
+ }
+
+ function setP(uint _n) payable {
+ value = _n;
+ }
+
+ function setNP(uint _n) {
+ value = _n;
+ }
+
+ function get () constant returns (uint) {
+ return value;
+ }
+ }
+
+This contract is very basic. The goal is to quickly start to create and to interact with a sample contract.
+
+Deploying an instance
+---------------------
+
+The ``Compile tab`` displays information related to the current contract (note that there can be more than one) (see :doc:`../compile_tab`).
+
+Moving on, in the ``Run tab`` select, ``JavaScript VM`` to specify that you are going to deploy an instance of the contract in the ``JavaScript VM`` state.
+
+.. image:: images/remix_quickstart_javascriptvm_creation.png
+
+The constructor of ``testContract`` needs a parameter (of type ``uint``). Give any value and click on ``Create``.
+
+The transaction which deploys the instance of ``testContract`` is created.
+
+In a "normal" blockchain, it can take several seconds to execute. This is the time for the transaction to be mined. However, because we are using the ``JavaScript VM``, our execution is immediate.
+
+The terminal will inform you about the transaction. You can see details there and start debugging.
+
+The newly created instance is displayed in the ``run tab``.
+
+.. image:: images/remix_quickstart_javascriptvm_creationTransaction.png
+
+Interacting with an instance
+----------------------------
+
+This new instance contains 3 actions which corresponds to the 3 functions (``setP``, ``setPN``, ``get``).
+Clicking on ``SetP`` or ``SetPN`` will create a new transaction.
+
+Note that ``SetP`` is ``payable`` (red action) : it is possible to send value (Ether) to the contract.
+
+``SetPN`` is not payable (light red action) : it is not possible to send value (Ether) to the contract.
+
+Clicking on ``get`` will not execute a transaction (blue action). It is not necessary to do so because ``get`` does not modify the state (variable ``value``) of this instance.
+
+As ``get`` is ``constant`` you can see the return value just below the action.
+
+.. image:: images/remix_quickstart_javascriptvm_callinginstance.png
+
+
+
\ No newline at end of file
diff --git a/docs_old/remix1.png b/docs_old/remix1.png
new file mode 100644
index 0000000000..7575d34f6c
Binary files /dev/null and b/docs_old/remix1.png differ
diff --git a/docs_old/remix2.png b/docs_old/remix2.png
new file mode 100644
index 0000000000..17a53affd9
Binary files /dev/null and b/docs_old/remix2.png differ
diff --git a/docs_old/remix3.png b/docs_old/remix3.png
new file mode 100644
index 0000000000..060352a755
Binary files /dev/null and b/docs_old/remix3.png differ
diff --git a/docs_old/remix4.png b/docs_old/remix4.png
new file mode 100644
index 0000000000..404ed50572
Binary files /dev/null and b/docs_old/remix4.png differ
diff --git a/docs_old/remix5.png b/docs_old/remix5.png
new file mode 100644
index 0000000000..4297d58adc
Binary files /dev/null and b/docs_old/remix5.png differ
diff --git a/docs_old/remix_breakpoint.png b/docs_old/remix_breakpoint.png
new file mode 100644
index 0000000000..c755f61348
Binary files /dev/null and b/docs_old/remix_breakpoint.png differ
diff --git a/docs_old/remix_debuginstructions.png b/docs_old/remix_debuginstructions.png
new file mode 100644
index 0000000000..bb36decbaf
Binary files /dev/null and b/docs_old/remix_debuginstructions.png differ
diff --git a/docs_old/remix_debugtransactioninfo.png b/docs_old/remix_debugtransactioninfo.png
new file mode 100644
index 0000000000..92d2e33100
Binary files /dev/null and b/docs_old/remix_debugtransactioninfo.png differ
diff --git a/docs_old/remix_enterdebugsession.png b/docs_old/remix_enterdebugsession.png
new file mode 100644
index 0000000000..77acc520ee
Binary files /dev/null and b/docs_old/remix_enterdebugsession.png differ
diff --git a/docs_old/remix_executionexception.png b/docs_old/remix_executionexception.png
new file mode 100644
index 0000000000..9f28396b8a
Binary files /dev/null and b/docs_old/remix_executionexception.png differ
diff --git a/docs_old/remix_navigation.png b/docs_old/remix_navigation.png
new file mode 100644
index 0000000000..d4187a462a
Binary files /dev/null and b/docs_old/remix_navigation.png differ
diff --git a/docs_old/remix_soliditylocals.png b/docs_old/remix_soliditylocals.png
new file mode 100644
index 0000000000..bfa627d1a4
Binary files /dev/null and b/docs_old/remix_soliditylocals.png differ
diff --git a/docs_old/remix_soliditystate.png b/docs_old/remix_soliditystate.png
new file mode 100644
index 0000000000..789df079e2
Binary files /dev/null and b/docs_old/remix_soliditystate.png differ
diff --git a/docs_old/remix_startdebugging.png b/docs_old/remix_startdebugging.png
new file mode 100644
index 0000000000..bf5287a5c3
Binary files /dev/null and b/docs_old/remix_startdebugging.png differ
diff --git a/docs_old/remix_stepdetail.png b/docs_old/remix_stepdetail.png
new file mode 100644
index 0000000000..efde21304e
Binary files /dev/null and b/docs_old/remix_stepdetail.png differ
diff --git a/docs_old/remix_valueinput.png b/docs_old/remix_valueinput.png
new file mode 100644
index 0000000000..381ebfc86a
Binary files /dev/null and b/docs_old/remix_valueinput.png differ
diff --git a/docs_old/remixd_alert.png b/docs_old/remixd_alert.png
new file mode 100644
index 0000000000..d64e6f7214
Binary files /dev/null and b/docs_old/remixd_alert.png differ
diff --git a/docs_old/remixd_connectionok.png b/docs_old/remixd_connectionok.png
new file mode 100644
index 0000000000..e8381f55df
Binary files /dev/null and b/docs_old/remixd_connectionok.png differ
diff --git a/docs_old/remixd_noconnection.png b/docs_old/remixd_noconnection.png
new file mode 100644
index 0000000000..b6cd6f5b40
Binary files /dev/null and b/docs_old/remixd_noconnection.png differ
diff --git a/docs_old/run_tab.rst b/docs_old/run_tab.rst
new file mode 100644
index 0000000000..516ef35a5c
--- /dev/null
+++ b/docs_old/run_tab.rst
@@ -0,0 +1,251 @@
+Running transactions
+====================
+
+The Run tab is an important section of Remix. It allows you to send transactions to the current environment.
+
+.. image:: images/remix_runtab.png
+
+Run Setup
+---------
+
+The following settings allow you to directly influence the transaction execution:
+
+ - Environment:
+ - ``JavaScript VM``: All the transactions will be executed in a sandbox blockchain in the browser. This means nothing will be persisted and a page reload will restart a new blockchain from scratch, the old one will not be saved.
+
+ - ``Injected Provider``: Remix will connect to an injected web3 provider. ``Mist`` and ``Metamask`` are example of providers that inject web3, thus can be used with this option.
+
+ - ``Web3 Provider``: Remix will connect to a remote node. You will need to provide the URL address to the selected provider: geth, parity or any Ethereum client.
+
+ - Account: the list of accounts associated with the current environment (and their associated balances).
+ - Gas Limit: the maximum amount of gas that can be set for all the transactions created in Remix.
+ - Value: the amount of value for the next created transaction (this value is always reset to 0 after each transaction execution).
+
+ .. image:: images/remix_runtab_example.png
+
+Initiate Instance
+-----------------
+
+This section contains the list of compiled contracts and 2 actions:
+
+- ``At Address`` assumes the given address is an instance of the selected contract. It is then possible to interact with an already deployed contract. There's no check at this point, so be careful when using this feature, and be sure you trust the contract at that address.
+
+- ``Create`` send a transaction that deploys the selected contract. When the transaction is mined, the newly created instance will be added (this might take several seconds). Note that if the ``constructor`` has parameters, you need to specify them.
+
+Pending Instances
+-----------------
+
+Validating a transaction take several seconds. During this time, the GUI shows it in a pending mode. When transaction is mined the number of pending transactions is updated
+and the transaction is added to the log (see :doc:`../terminal`)
+
+Instance List
+-------------
+
+This section contains a list of instances to interact with.
+
+Several cases apply:
+ - The called function is declared as ``constant`` or ``pure`` in Solidity. The action has a blue background, clicking it does not create a new transaction. Clicking it is not necessary because there are not state changes - but it will update the return value of the function.
+
+ - The called function has no special keywords. The action has a light red background, clicking on does create a new transaction. But this transaction cannot accept any amount of Ether.
+
+ - The called function is declared as ``payable`` in Solidity. The action has a red background, clicking it does create a new transaction and this transaction can accept value.
+
+For more information about Solidity modifier, see `Solidity modifier `_ .
+
+If a function requires input parameters, it is required to specify them.
+
+USING ABI
+---------
+
+Using ``Create`` or ``At Address`` is a classic use case of Remix. It is possible though to interact with a contract by using its ABI. The ABI is a JSON array which describe its interface.
+
+To interact with a contract using the ABI, create a new file in Remix with extension ``*.abi`` and copy the ABI content to it.
+Then in the input next to ``At Address``, put the Address of the contract you want to interact with. Click on ``At Address``,
+a new "connection" with the contract will popup below.
+
+USING THE RECORDER
+------------------
+
+The Recorder allows to save a bunch of transactions in a JSON file and rerun them later either in the same environment or in another.
+
+Saving to JSON allows to easily check the transaction list, tweak input parameters, change linked library, etc...
+
+We can find many use cases for the recorder, for instance:
+ - After having coded and tested contracts in a constrained environment (like the JavaScript VM), it could be interesting to redeploy them easily in a more persisted environment (like a Geth node) in order to check whether everything behaves normally in a classic environment.
+ - Deploying contract does often require more than creating one transaction.
+ - Working in a dev environment does often require to setup the state in a first place.
+
+
+.. image:: images/remix_recorder.png
+
+Saving a record ends up with the creation of this type of content (see below):
+
+In that specific record, 3 transactions are executed:
+
+The first corresponds to the deployment of the lib ``testLib``.
+
+The second corresponds to the deployment of the contract ``test``, the first parameter of the constructor is set to 11.
+That contract depends on a library. The linkage is done using the property ``linkReferences``.
+In that case we use the addres of the previously created library : ``created{1512830014773}``. the number is
+the id (timestamp) of the transaction that leads to the creation of the library.
+
+The third parameter corresponds to the call to te function ``set`` of the contract ``test`` (the property to is set to: ``created{1512830015080}``) . Input parameters are ``1`` and ``0xca35b7d915458ef540ade6068dfe2f44e8fa733c``
+
+all these transactions are created using the value of the accounts ``account{0}``.
+
+.. code-block:: none
+
+ {
+ "accounts": {
+ "account{0}": "0xca35b7d915458ef540ade6068dfe2f44e8fa733c"
+ },
+ "linkReferences": {
+ "testLib": "created{1512830014773}"
+ },
+ "transactions": [
+ {
+ "timestamp": 1512830014773,
+ "record": {
+ "value": "0",
+ "parameters": [],
+ "abi": "0xbc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a",
+ "contractName": "testLib",
+ "bytecode": "60606040523415600e57600080fd5b60968061001c6000396000f300606060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680636d4ce63c146044575b600080fd5b604a6060565b6040518082815260200191505060405180910390f35b6000610d809050905600a165627a7a7230582022d123b15248b8176151f8d45c2dc132063bcc9bb8d5cd652aea7efae362c8050029",
+ "linkReferences": {},
+ "type": "constructor",
+ "from": "account{0}"
+ }
+ },
+ {
+ "timestamp": 1512830015080,
+ "record": {
+ "value": "100",
+ "parameters": [
+ 11
+ ],
+ "abi": "0xc41589e7559804ea4a2080dad19d876a024ccb05117835447d72ce08c1d020ec",
+ "contractName": "test",
+ "bytecode": "60606040526040516020806102b183398101604052808051906020019091905050806000819055505061027a806100376000396000f300606060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680632f30c6f61461006757806338cc48311461009e57806362738998146100f357806387cc10e11461011c575b600080fd5b61009c600480803590602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610145565b005b34156100a957600080fd5b6100b1610191565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156100fe57600080fd5b6101066101bb565b6040518082815260200191505060405180910390f35b341561012757600080fd5b61012f6101c4565b6040518082815260200191505060405180910390f35b8160008190555080600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505050565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008054905090565b600073__browser/ballot.sol:testLib____________636d4ce63c6000604051602001526040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160206040518083038186803b151561022e57600080fd5b6102c65a03f4151561023f57600080fd5b505050604051805190509050905600a165627a7a72305820e0b2510bb2890a0334bfe5613d96db3e72442e63b514cdeaee8fc2c6bbd19d3a0029",
+ "linkReferences": {
+ "browser/ballot.sol": {
+ "testLib": [
+ {
+ "length": 20,
+ "start": 511
+ }
+ ]
+ }
+ },
+ "name": "",
+ "type": "constructor",
+ "from": "account{0}"
+ }
+ },
+ {
+ "timestamp": 1512830034180,
+ "record": {
+ "value": "1000000000000000000",
+ "parameters": [
+ 1,
+ "0xca35b7d915458ef540ade6068dfe2f44e8fa733c"
+ ],
+ "to": "created{1512830015080}",
+ "abi": "0xc41589e7559804ea4a2080dad19d876a024ccb05117835447d72ce08c1d020ec",
+ "name": "set",
+ "type": "function",
+ "from": "account{0}"
+ }
+ }
+ ],
+ "abis": {
+ "0xbc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a": [
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "get",
+ "outputs": [
+ {
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ }
+ ],
+ "0xc41589e7559804ea4a2080dad19d876a024ccb05117835447d72ce08c1d020ec": [
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "getInt",
+ "outputs": [
+ {
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "getFromLib",
+ "outputs": [
+ {
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "getAddress",
+ "outputs": [
+ {
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "_t",
+ "type": "uint256"
+ },
+ {
+ "name": "_add",
+ "type": "address"
+ }
+ ],
+ "name": "set",
+ "outputs": [],
+ "payable": true,
+ "stateMutability": "payable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "name": "_r",
+ "type": "uint256"
+ }
+ ],
+ "payable": true,
+ "stateMutability": "payable",
+ "type": "constructor"
+ }
+ ]
+ }
+ }
\ No newline at end of file
diff --git a/docs_old/settings_tab.rst b/docs_old/settings_tab.rst
new file mode 100644
index 0000000000..cc32a9729f
--- /dev/null
+++ b/docs_old/settings_tab.rst
@@ -0,0 +1,13 @@
+Settings
+========
+
+This section displays the current compiler version and allows one to change to another version.
+
+.. image:: images/remix_settingstab.png
+
+Settings, available here:
+
+ - Text wrap: controls if the text in the editor should be wrapped.
+ - Enable optimization: defines if the compiler should enable optimization during compilation. Enabling this option saves execution gas.
+ It is useful to enable optimization for contracts ready to be deployed in production
+ but could lead to some inconsistencies when debugging such a contract.
diff --git a/docs_old/solidity_editor.rst b/docs_old/solidity_editor.rst
new file mode 100644
index 0000000000..cc1928664f
--- /dev/null
+++ b/docs_old/solidity_editor.rst
@@ -0,0 +1,13 @@
+Solidity Editor
+===============
+
+The Remix editor recompiles the code each time the current file is changed or another file is selected. It also provides syntax highlighting mapped to solidity keywords.
+
+.. image:: images/remix_editor.png
+
+Here's the list of some important features:
+
+- It display opened files as tabs.
+- Compilation Warning and Error are displayed in the gutter
+- Remix saves the current file continuously (5s after the last changes)
+- +/- on the top left corner enable you to increase/decrease the font size of the editor
diff --git a/docs_old/support_tab.rst b/docs_old/support_tab.rst
new file mode 100644
index 0000000000..fcc1199740
--- /dev/null
+++ b/docs_old/support_tab.rst
@@ -0,0 +1,6 @@
+Support
+=======
+
+This section provides a link to Remix Issues where users can report a bug or suggest a feature, as well as providing other useful links. It also displays a `Remix developers' channel `_
+
+.. image:: images/remix_supporttab.png
diff --git a/docs_old/tabs_panel.rst b/docs_old/tabs_panel.rst
new file mode 100644
index 0000000000..dcb4e0f3a8
--- /dev/null
+++ b/docs_old/tabs_panel.rst
@@ -0,0 +1,9 @@
+Tabs Panel
+==========
+
+- :doc:`../compile_tab`
+- :doc:`../run_tab`
+- :doc:`../settings_tab`
+- :doc:`../debugger_tab`
+- :doc:`../analysis_tab`
+- :doc:`../support_tab`
diff --git a/docs_old/terminal.rst b/docs_old/terminal.rst
new file mode 100644
index 0000000000..46e752f5bd
--- /dev/null
+++ b/docs_old/terminal.rst
@@ -0,0 +1,14 @@
+Terminal
+========
+
+.. image:: images/remix_terminal.png
+
+Features, available in the terminal:
+
+- It integrates a JavaScript interpreter and the ``web3`` object. It enables the execution of the JavaScript script which interacts with the current context. (note that ``web3`` is only available if the ``web provider`` or ``injected provider`` mode is selected).
+
+- It displays important actions made while interacting with the Remix IDE (i.e. sending a new transaction).
+
+- It displays transactions that are mined in the current context. You can choose to display all transactions or only transactions that refers to the contracts Remix knows (e.g transaction created from the Remix IDE).
+
+- It allows searching for the data and clearing the logs from the terminal.
diff --git a/docs_old/tuto_basicimport.png b/docs_old/tuto_basicimport.png
new file mode 100644
index 0000000000..81a8009336
Binary files /dev/null and b/docs_old/tuto_basicimport.png differ
diff --git a/docs_old/tuto_importgit.png b/docs_old/tuto_importgit.png
new file mode 100644
index 0000000000..022eb4b011
Binary files /dev/null and b/docs_old/tuto_importgit.png differ
diff --git a/docs_old/tuto_importswarm.png b/docs_old/tuto_importswarm.png
new file mode 100644
index 0000000000..01fba3e646
Binary files /dev/null and b/docs_old/tuto_importswarm.png differ
diff --git a/docs_old/tutorial_debug.rst b/docs_old/tutorial_debug.rst
new file mode 100644
index 0000000000..8030187f5c
--- /dev/null
+++ b/docs_old/tutorial_debug.rst
@@ -0,0 +1,204 @@
+Debugging a Transaction
+=======================
+
+.. _tutorial-debug:
+
+The goal of this tutorial is to explain how to debug transaction using Remix.
+
+Start debugging
+---------------
+
+There are two different ways to start debugging, each way correspond to a different use case.
+
+From the Transaction GUI
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+We will not explain in detail here how to write or deploy contract.
+Let us start with a basic contract (replace this one by your's):
+
+.. code-block:: none
+
+ contract Donation {
+ address owner;
+ event fundMoved(address _to, uint _amount);
+ modifier onlyowner { if (msg.sender == owner) _; }
+ address[] _giver;
+ uint[] _values;
+
+ function Donation() {
+ owner = msg.sender;
+ }
+
+ function donate() payable {
+ addGiver(msg.value);
+ }
+
+ function moveFund(address _to, uint _amount) onlyowner {
+ uint balance = this.balance;
+ uint amount = _amount;
+ if (_amount <= this.balance) {
+ if (_to.send(this.balance)) {
+ fundMoved(_to, _amount);
+ } else {
+ throw;
+ }
+ } else {
+ throw;
+ }
+ }
+
+ function addGiver(uint _amount) internal {
+ _giver.push(msg.sender);
+ _values.push(_amount);
+ }
+ }
+
+For the purpose of this tutorial, we will run the ``JavaScript VM`` (that's the default mode when you don't use Remix with Mist or Metamask). This simulates a custom blockchain. You could do the same using a proper backend node.
+
+Now, let's deploy the contract:
+
+Right panel / Red button ``Create``
+
+.. image:: remix1.png
+
+Then we should call the ``Donate`` function (that will send Ether to the contract).
+
+Let's set the amount of Ether:
+
+Right panel / second tab from the left - fill in the ´´value´´ input (´1 ether´ for instance)
+
+.. image:: remix_valueinput.png
+
+Then click on ``Donate``. As we are using the ``JavaScript VM``, everything goes almost instantly.
+
+Remix displays also some information related to each transaction result. In the terminal, the transaction is logged and you can start debugging it.
+
+.. image:: remix_startdebugging.png
+
+From the Debugger
+~~~~~~~~~~~~~~~~~
+
+The debugger can be found in the right panel / 5th tab from the left.
+
+You can start a debug session by providing either a ``transaction hash`` or a ``block number`` and ``transaction index``.
+
+.. image:: remix3.png
+
+Click the ``play`` button to start debugging.
+
+Using the debugger
+------------------
+
+The debugger allows one to see detailed informations about the transaction's execution. It uses the editor (left panel) to display the location
+in the source code where the current execution is.
+
+The transaction panel displays basic information about the current transaction.
+
+.. image:: remix_debugtransactioninfo.png
+
+The navigation part contains a slider and buttons that can be used to step through the transaction execution.
+
+From the left to the right:
+
+step over back, step into back, step into forward, step over forward, jump out (jump out of the current call), jump to the previous breakpoint, jump to the next breakpoint.
+
+.. image:: remix_navigation.png
+
+11 panels give detailed information about the execution:
+
+Instructions
+~~~~~~~~~~~~
+
+.. image:: remix_debuginstructions.png
+
+The Instructions panel displays the bytecode of the current executing contract- with the current step highlighted.
+
+Important note:
+When this panel is hidden, the slider will have a courser granularity and only stop at expression boundaries, even if they are compiled into multiple EVM instructions.
+When the panel is displayed, it will be possible to step over every instruction, even those that refers to the same expression.
+
+Solidity Locals
+~~~~~~~~~~~~~~~
+
+.. image:: remix_soliditylocals.png
+
+The Solidity Locals panel displays local variables associated with the current context.
+
+Solidity State
+~~~~~~~~~~~~~~
+
+.. image:: remix_soliditystate.png
+
+The Solidity State panel displays state variables of the current executing contract.
+
+Low level panels
+~~~~~~~~~~~~~~~~
+
+These panels display low level informations about the execution:
+
+ - Stack
+ - Storages Changes
+ - Memory
+ - Call Data
+ - Call Stack
+ - Return Value (only if the current step is a RETURN opcode)
+ - Full Storages Changes (only at the end of the execution - display every storage change of every modified contract)
+
+Reverted Transaction
+~~~~~~~~~~~~~~~~~~~~
+
+A transaction could be reverted (either because of out of gas exception, Solidity ``throw`` or low level exception).
+
+In that case it is important to be aware of the exception and to locate where the exception is in the source code.
+
+Remix will warn you when the execution throws an exception. The ``warning`` button will jump to the last opcode before the exception happened.
+
+.. image:: remix_executionexception.png
+
+Breakpoints
+~~~~~~~~~~~
+
+The two last buttons from the navigation area are used to jump either back to the previous breakpoint or forward to the next breakpoint.
+
+Breakpoints can be added and removed by clicking on the line number.
+
+.. image:: remix_breakpoint.png
+
+When a debug session is started, the execution will jump to the first encountered breakpoint.
+
+Important note:
+If you add a breakpoint to a line that declares a variable, it might be triggered twice: Once for initializing the
+variable to zero and second time for assigning the actual value.
+As an example, assume you are debugging the following contract:
+
+.. code-block:: none
+
+ contract ctr {
+ function hid () {
+ uint p = 45;
+ uint m;
+ m = 89;
+ uint l = 34;
+ }
+ }
+
+And let's says that breakpoints are set for the lines
+
+``uint p = 45;``
+
+``m = 89;``
+
+``uint l = 34;``
+
+
+then clicking on ``Jump to next breakpoint`` will stop at the following lines in the given order:
+
+ ``uint p = 45;`` (declaration of p)
+
+ ``uint l = 34;`` (declaration of l)
+
+ ``uint p = 45;`` (45 assigned to p)
+
+ ``m = 89;`` (89 assigned to m)
+
+ ``uint l = 34;`` (34 assigned to l)
diff --git a/docs_old/tutorial_import.rst b/docs_old/tutorial_import.rst
new file mode 100644
index 0000000000..2a74046e4c
--- /dev/null
+++ b/docs_old/tutorial_import.rst
@@ -0,0 +1,35 @@
+Importing Source Files in Solidity
+==================================
+
+.. _tutorial-import:
+
+This tutorial will show you how to import local and external files.
+
+The compilation result will also contain contracts implemented in the imported files.
+
+For a detailed explanation of the ``import`` keyword see the ``Solidity``
+`documentation `_
+
+Importing a local file
+----------------------
+
+Other files in Remix can be imported just by specifying their path.
+Please use `./` for relative paths to increase portability.
+
+.. image:: tuto_basicimport.png
+
+Importing from Github
+----------------------
+
+It is possible to import files directly from github with URLs like
+``https://github.com///``.
+
+.. image:: tuto_importgit.png
+
+Importing from Swarm
+--------------------
+
+Files can be imported using all URLs supported by swarm. If you do not have a swarm
+node, swarm-gateways.net will be used instead.
+
+.. image:: tuto_importswarm.png
diff --git a/docs_old/tutorial_mist.rst b/docs_old/tutorial_mist.rst
new file mode 100644
index 0000000000..526882d7fc
--- /dev/null
+++ b/docs_old/tutorial_mist.rst
@@ -0,0 +1,289 @@
+Debugging a Dapp using Remix - Mist - Geth
+===================================================
+
+.. _tutorial-mist-geth:
+
+The ultimate goal of this tutorial is to debug transactions that have been created by a dapp front end.
+
+It is easy in Remix to debug a transaction created from its own GUI. However, setting up an environment that allows you to
+debug transactions created outside of Remix, require a bit more of complexity.
+
+We will need four tools for that :
+
+ - Geth - this is the center piece and provides the blockchain environment. We will basically run geth in a `dev` mode.
+
+ - Mist - this is the Ethereum dapp browser. We will use it to browse our front end.
+
+ - Remix - this is the Ethereum IDE. We will use it to develop our Solidity contract.
+
+ - Any code editor you want - in order to write your front end :)
+
+Install the environment
+-----------------------
+
+Install Mist
+~~~~~~~~~~~~
+
+Mist is the Ethereum browser and the entry point of a Dapp.
+
+Please download `the latest version `_ (at least 0.8.9).
+
+Basically we will always run our front end in Mist (note that it is also possible to use `Metamask `_).
+
+Install Geth
+~~~~~~~~~~~~
+
+`Geth `_ is the official Ethereum client.
+
+Running the environment
+-----------------------
+
+Run Geth
+~~~~~~~~
+
+We will run a test node. This node will have a new empty state and will not be synced to the main or ropsten network.
+
+::
+
+ geth --ipcpath /geth.ipc --datadir --dev console
+
+
+```` is the folder where keys and chain data will be stored.
+
+``--ipcpath`` defines the end point that other apps (like Mist) use to talk to geth.
+
+``--datadir`` specifies the data directory.
+
+``--dev`` sets the node into private chain mode and adds some debugging flags.
+
+Then we need to create accounts and mine a bit to generate some Ether:
+
+::
+
+ // from the geth console :
+ personal.newAccount() // You can execute this command several time if you need more than one account.
+ miner.start() // generate some Ether.
+ miner.stop() // stop mining after 30s-60s - we could also keep mining.
+
+Next time we run Geth, we will only need to mine transactions (no need to recreate account).
+
+Run Mist
+~~~~~~~~
+
+If we run Mist without any argument, its internal Geth node will run. As we have our own we need to specify the ipc path of the node installed above.
+
+::
+
+ mist --rpc /geth.ipc
+
+(yes the option is --rpc)
+
+Once Mist is started, verify that it is connected to the test node (that's very important!!).
+
+On the bottom left, check that the network is ``Private-net`` and that the block number is the same as reported by the test node we are currently running. Run the following command in the Geth Console to check: `web3.eth.blockNumber`.
+
+.. image:: mist1.png
+
+Clicking on `Wallet` will allow you to send transactions and check account balances (if you are currently mining you should see the balance increasing).
+
+Starting Remix
+~~~~~~~~~~~~~~
+
+In Mist click on ``Develop`` / ``Open Remix IDE``
+
+Remix will open in a new window. If this is the first time it is run, the ``Ballot`` contract will be loaded.
+
+Now, we need to check if Remix is connected to Mist:
+
+Right panel / third tab from the left, ``Injected Provider`` should be checked.
+
+.. image:: remix4.png
+
+Right panel / second tab from the left, ``Transaction Origin`` should contain accounts we have previously created in Geth.
+
+.. image:: remix5.png
+
+Developing contract / front end
+--------------------------------
+
+Donation contract - Dapp Back end
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Here is a sample solidity contract.
+
+Copy and paste the following inside remix:
+
+.. code-block:: none
+
+ contract Donation {
+ address owner;
+ event fundMoved(address _to, uint _amount);
+ modifier onlyowner { if (msg.sender == owner) _; }
+ address[] _giver;
+ uint[] _values;
+
+ function Donation() {
+ owner = msg.sender;
+ }
+
+ function donate() payable {
+ addGiver(msg.value);
+ }
+
+ function moveFund(address _to, uint _amount) onlyowner {
+ uint balance = this.balance;
+ uint amount = _amount;
+ if (_amount <= this.balance) {
+ if (_to.send(this.balance)) {
+ fundMoved(_to, _amount);
+ } else {
+ throw;
+ }
+ } else {
+ throw;
+ }
+ }
+
+ function addGiver(uint _amount) internal {
+ _giver.push(msg.sender);
+ _values.push(_amount);
+ }
+ }
+
+
+Dapp Front end
+~~~~~~~~~~~~~~
+
+and here is the front end:
+
+.. code-block:: none
+
+
+
Donation Contract
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+I would suggest serving this file using ``http-serve``, but you can use any web server you like.
+
+Example: Dapp Front End https://github.com/ltfschoen/dapp_front_end
+
+Important notice !
+~~~~~~~~~~~~~~~~~~
+
+The variable ``contractspec`` contains the abi of the ``donation`` contract. This means that if you change something in the contract interface (function names, parameters, ...)
+you need to copy the new abi from remix to the front end.
+
+Deploying
+---------
+
+Right panel / Red button ``Create``
+
+.. image:: remix1.png
+
+This creates a new transaction that deploys the ``Donation`` contract (Mist will ask for the usual passphrase check).
+
+Wait for the transaction to be mined (don't forget to activate mining ``miner.start()``).
+Once this is done, you can use it by executing the ``moveFund`` and ``donate`` function. But this is not what we
+want to achieve. We want to run and debug those functions from the front end.
+
+Remix also display the address of the contract. Save it, we'll need this address later.
+
+.. image:: remix2.png
+
+Debugging
+---------
+
+From Mist, browse the above front end.
+In the first field, paste the address of the newly created contract. Now, let's call the first function (label ``give``).
+
+You will need an account and a value.
+
+The account could be any account that is declared in the Wallet section of Mist. This is the sender of the transaction that we are going to create.
+The value should be no more than the actual balance of the account - the unit is in `wei`, so just put ``100`` (100 wei), that should be fine.
+
+Click on ``Give`` and wait for the transaction to be mined.
+
+The HTML block with id ``log`` is filled by all the transactions created from the front end.
+It was easier for the purpose of this tutorial to just log transactions in a div but you can have your own logging mechanism.
+
+There is only one field that we need, this is the ``transactionHash``.
+
+Copy it and switch to Remix. On the right side, the fifth panel shows a small "bug" icon, that is the debugger.
+
+Paste the hash into the transaction field and click on the ``play`` button.
+
+.. image:: remix3.png
+
+You are now entering a debug session for the call to ``donate``.
+
+Debugging in Remix is easier than with common tools like gdb because you can freely move in time.
+Use the slider to change the current step and click on the panels below to expand them and explore the curret state, local variables, etc.
+There are also breakpoints to move between sections of the code quickly, but more on all that later.
+
+
+At the time of this writing, there is an issue that could break the contract creation.
+The a workaround for that at https://github.com/ethereum/go-ethereum/issues/3653 .
+Please follow the workaround or wait for this issue to be closed.
+
+Also, although retrieving a contract's storage when Remix is using the JavaScript VM is working well,
+there is still work to be done when Remix is using eth or geth as backend.
diff --git a/docs_old/tutorial_remixd_filesystem.rst b/docs_old/tutorial_remixd_filesystem.rst
new file mode 100644
index 0000000000..d045279307
--- /dev/null
+++ b/docs_old/tutorial_remixd_filesystem.rst
@@ -0,0 +1,37 @@
+Accessing a shared folder in Remix IDE using Remixd
+===================================================
+
+.. _tutorial-remixd-filesystem:
+
+Remixd is an npm module. Its purpose is to give the remix web application access to a folder from your local computer.
+
+The code of Remixd can be checked out `here `_ .
+
+Remixd can be globally installed using the following command: ``npm install -g remixd``.
+
+Then ``remixd -s `` will start Remixd and share the given folder.
+
+The folder is shared using a websocket connection between ``Remix IDE`` and ``Remixd``.
+
+Be sure the user executing Remix has read/write permission on the folder.
+
+.. warning::
+ Remixd provides full read and write access to the given folder for any application that can access the TCP port 65520 on your local host.
+
+From ``Remix IDE``, you will need to activate the connection.
+
+Click on the ``localhost connection`` icon:
+
+.. image:: remixd_noconnection.png
+
+A modal dialog will ask confirmation
+
+.. image:: remixd_alert.png
+
+Accepting this dialog will start a session. Once the connection is made, the status will update and the connection icon should shows up in green.
+
+Hovering the icon will give more connection status information.
+
+At this point if the connection is successful, the shared folder will be available in the file explorer.
+
+.. image:: remixd_connectionok.png
diff --git a/lerna.json b/lerna.json
new file mode 100644
index 0000000000..6a1f75ae93
--- /dev/null
+++ b/lerna.json
@@ -0,0 +1,11 @@
+{
+ "lerna": "2.10.2",
+ "packages": [
+ "remix-debug",
+ "remix-debugger",
+ "remix-lib",
+ "remix-solidity",
+ "remix-analyzer"
+ ],
+ "version": "independent"
+}
diff --git a/package.json b/package.json
index d3b2bd054d..6caf32cecb 100644
--- a/package.json
+++ b/package.json
@@ -1,61 +1,9 @@
{
- "name": "ethereum-remix",
- "version": "0.0.2-alpha.0.0.7",
- "description": "Ethereum IDE and tools for the web",
- "contributors": [
- {
- "name": "Yann Levreau",
- "email": "yann@ethdev.com"
- },
- {
- "name": "Liana Husikyan",
- "email": "liana@ethdev.com"
- }
- ],
- "main": "./src/index.js",
- "dependencies": {
- "which": "^1.2.10"
- },
- "devDependencies": {
- "browserify": "^13.0.1",
- "ethereumjs-util": "^4.5.0",
- "http-server": "^0.9.0",
- "nightwatch": "^0.9.5",
- "standard": "^7.0.1",
- "standard-reporter": "^1.0.5",
- "tape": "^4.6.0",
- "web3": "^0.15.3",
- "yo-yo": "^1.2.1",
- "yo-yoify": "^3.1.0"
- },
- "scripts": {
- "start_node": "./runNode.sh",
- "start_eth": "eth -j --rpccorsdomain '*'",
- "start_geth": "geth --rpc --rpcapi 'web3,eth,debug' --rpcport 8545 --rpccorsdomain '*'",
- "build": "mkdir -p build; browserify src/index.js -g yo-yoify -o build/app.js",
- "test": "standard && tape ./test/tests.js",
- "serve": "http-server .",
- "nightwatch_local": "nightwatch --config nightwatch.js --env local",
- "nightwatch_remote_firefox": "nightwatch --config nightwatch.js --env default",
- "nightwatch_remote_chrome": "nightwatch --config nightwatch.js --env chrome",
- "nightwatch_remote_safari": "nightwatch --config nightwatch.js --env safari",
- "nightwatch_remote_ie": "nightwatch --config nightwatch.js --env ie"
- },
- "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/*"
- ]
- }
+ "devDependencies": {
+ "lerna": "^2.10.2"
+ },
+ "scripts": {
+ "bootstrap": "lerna bootstrap",
+ "publish": "lerna publish"
+ }
}
diff --git a/remix-analyzer/index.js b/remix-analyzer/index.js
new file mode 100644
index 0000000000..3fd9c0fd4a
--- /dev/null
+++ b/remix-analyzer/index.js
@@ -0,0 +1,5 @@
+var CodeAnalysis = require('./src/analysis/staticAnalysisRunner')
+
+module.exports = {
+ CodeAnalysis: CodeAnalysis
+}
diff --git a/remix-analyzer/package.json b/remix-analyzer/package.json
new file mode 100644
index 0000000000..35815bbc6d
--- /dev/null
+++ b/remix-analyzer/package.json
@@ -0,0 +1,42 @@
+{
+ "name": "remix-analyzer",
+ "version": "0.1.0",
+ "description": "Remix Analyzer",
+ "main": "./index.js",
+ "contributors": [
+ {
+ "name": "Alex Beregszaszi",
+ "email": "alex@rtfs.hu"
+ },
+ {
+ "name": "Iuri Matias",
+ "email": "iuri@ethereum.org"
+ },
+ {
+ "name": "Yann Levreau",
+ "email": "yann@ethdev.com"
+ }
+ ],
+ "dependencies": {
+ "babel-eslint": "^7.1.1",
+ "babel-plugin-transform-object-assign": "^6.22.0",
+ "remix-lib": "^0.2.9",
+ "babel-preset-es2015": "^6.24.0",
+ "solc": "^0.4.24",
+ "standard": "^7.0.1",
+ "tape": "^4.6.0"
+ },
+ "scripts": {
+ "test": "standard && tape ./test/tests.js"
+ },
+ "standard": {
+ "ignore": [
+ "node_modules/*",
+ "soljson.js"
+ ],
+ "parser": "babel-eslint"
+ },
+ "author": "Remix Team",
+ "license": "MIT",
+ "homepage": "https://github.com/ethereum/remix#readme"
+}
diff --git a/remix-analyzer/src/analysis/modules/abstractAstView.js b/remix-analyzer/src/analysis/modules/abstractAstView.js
new file mode 100644
index 0000000000..ea28f77b32
--- /dev/null
+++ b/remix-analyzer/src/analysis/modules/abstractAstView.js
@@ -0,0 +1,177 @@
+var common = require('./staticAnalysisCommon')
+var AstWalker = require('remix-lib').AstWalker
+
+function abstractAstView () {
+ this.contracts = []
+ this.currentContractIndex = null
+ this.currentFunctionIndex = null
+ this.currentModifierIndex = null
+ this.isFunctionNotModifier = false
+ /*
+ file1: contract c{}
+ file2: import "file1" as x; contract c{}
+ therefore we have two contracts with the same name c. At the moment this is not handled because alias name "x" is not
+ available in the current AST implementation thus can not be resolved.
+ Additionally the fullQuallified function names e.g. [contractName].[functionName](param1Type, param2Type, ... ) must be prefixed to
+ fully support this and when inheritance is resolved it must include alias resolving e.g x.c = file1.c
+ */
+ this.multipleContractsWithSameName = false
+}
+
+/**
+ * Builds a higher level AST view. I creates a list with each contract as an object in it.
+ * Example contractsOut:
+ *
+ * {
+ * "node": {}, // actual AST Node of the contract
+ * "functions": [
+ * {
+ * "node": {}, // actual AST Node of the function
+ * "relevantNodes": [], // AST nodes in the function that are relevant for the anlysis of this function
+ * "modifierInvocations": [], // Modifier invocation AST nodes that are applied on this function
+ * "localVariables": [], // Local variable declaration nodes
+ * "parameters": [] // Parameter types of the function in order of definition
+ * }
+ * ],
+ * "modifiers": [], // Modifiers definded by the contract, format similar to functions
+ * "inheritsFrom": [], // Names of contract this one inherits from in order of definition
+ * "stateVariables": [] // AST nodes of all State variables
+ * }
+ *
+ * @relevantNodeFilter {ASTNode -> bool} function that selects relevant ast nodes for analysis on function level.
+ * @contractsOut {list} return list for high level AST view
+ * @return {ASTNode -> void} returns a function that can be used as visit function for static analysis modules, to build up a higher level AST view for further analysis.
+ */
+abstractAstView.prototype.build_visit = function (relevantNodeFilter) {
+ var that = this
+ return function (node) {
+ if (common.isContractDefinition(node)) {
+ setCurrentContract(that, {
+ node: node,
+ functions: [],
+ relevantNodes: [],
+ modifiers: [],
+ inheritsFrom: [],
+ stateVariables: common.getStateVariableDeclarationsFormContractNode(node)
+ })
+ } else if (common.isInheritanceSpecifier(node)) {
+ var currentContract = getCurrentContract(that)
+ var inheritsFromName = common.getInheritsFromName(node)
+ currentContract.inheritsFrom.push(inheritsFromName)
+ } else if (common.isFunctionDefinition(node)) {
+ setCurrentFunction(that, {
+ node: node,
+ relevantNodes: [],
+ modifierInvocations: [],
+ localVariables: getLocalVariables(node),
+ parameters: getLocalParameters(node),
+ returns: getReturnParameters(node)
+ })
+ // push back relevant nodes to their the current fn if any
+ getCurrentContract(that).relevantNodes.map((item) => {
+ if (item.referencedDeclaration === node.id) {
+ getCurrentFunction(that).relevantNodes.push(item.node)
+ }
+ })
+ } else if (common.isModifierDefinition(node)) {
+ setCurrentModifier(that, {
+ node: node,
+ relevantNodes: [],
+ localVariables: getLocalVariables(node),
+ parameters: getLocalParameters(node)
+ })
+ } else if (common.isModifierInvocation(node)) {
+ if (!that.isFunctionNotModifier) throw new Error('abstractAstView.js: Found modifier invocation outside of function scope.')
+ getCurrentFunction(that).modifierInvocations.push(node)
+ } else if (relevantNodeFilter(node)) {
+ var scope = (that.isFunctionNotModifier) ? getCurrentFunction(that) : getCurrentModifier(that)
+ if (scope) {
+ scope.relevantNodes.push(node)
+ } else {
+ scope = getCurrentContract(that) // if we are not in a function scope, add the node to the contract scope
+ if (scope && node.children[0] && node.children[0].attributes && node.children[0].attributes.referencedDeclaration) {
+ scope.relevantNodes.push({ referencedDeclaration: node.children[0].attributes.referencedDeclaration, node: node })
+ }
+ }
+ }
+ }
+}
+
+abstractAstView.prototype.build_report = function (wrap) {
+ var that = this
+ return function (compilationResult) {
+ resolveStateVariablesInHierarchy(that.contracts)
+ return wrap(that.contracts, that.multipleContractsWithSameName)
+ }
+}
+
+function resolveStateVariablesInHierarchy (contracts) {
+ contracts.map((c) => {
+ resolveStateVariablesInHierarchyForContract(c, contracts)
+ })
+}
+function resolveStateVariablesInHierarchyForContract (currentContract, contracts) {
+ currentContract.inheritsFrom.map((inheritsFromName) => {
+ // add variables from inherited contracts
+ var inheritsFrom = contracts.find((contract) => common.getContractName(contract.node) === inheritsFromName)
+ if (inheritsFrom) {
+ currentContract.stateVariables = currentContract.stateVariables.concat(inheritsFrom.stateVariables)
+ } else {
+ console.log('abstractAstView.js: could not find contract defintion inherited from ' + inheritsFromName)
+ }
+ })
+}
+function setCurrentContract (that, contract) {
+ var name = common.getContractName(contract.node)
+ if (that.contracts.map((c) => common.getContractName(c.node)).filter((n) => n === name).length > 0) {
+ console.log('abstractAstView.js: two or more contracts with the same name dectected, import aliases not supported at the moment')
+ that.multipleContractsWithSameName = true
+ }
+ that.currentContractIndex = (that.contracts.push(contract) - 1)
+}
+
+function setCurrentFunction (that, func) {
+ that.isFunctionNotModifier = true
+ that.currentFunctionIndex = (getCurrentContract(that).functions.push(func) - 1)
+}
+
+function setCurrentModifier (that, modi) {
+ that.isFunctionNotModifier = false
+ that.currentModifierIndex = (getCurrentContract(that).modifiers.push(modi) - 1)
+}
+
+function getCurrentContract (that) {
+ return that.contracts[that.currentContractIndex]
+}
+
+function getCurrentFunction (that) {
+ return getCurrentContract(that).functions[that.currentFunctionIndex]
+}
+
+function getCurrentModifier (that) {
+ return getCurrentContract(that).modifiers[that.currentModifierIndex]
+}
+
+function getLocalParameters (funcNode) {
+ return getLocalVariables(common.getFunctionOrModifierDefinitionParameterPart(funcNode)).map(common.getType)
+}
+
+function getReturnParameters (funcNode) {
+ return getLocalVariables(common.getFunctionOrModifierDefinitionReturnParameterPart(funcNode)).map((n) => {
+ return {
+ type: common.getType(n),
+ name: common.getDeclaredVariableName(n)
+ }
+ })
+}
+
+function getLocalVariables (funcNode) {
+ var locals = []
+ new AstWalker().walk(funcNode, {'*': function (node) {
+ if (common.isVariableDeclaration(node)) locals.push(node)
+ return true
+ }})
+ return locals
+}
+
+module.exports = abstractAstView
diff --git a/remix-analyzer/src/analysis/modules/assignAndCompare.js b/remix-analyzer/src/analysis/modules/assignAndCompare.js
new file mode 100644
index 0000000000..c9ec2e7820
--- /dev/null
+++ b/remix-analyzer/src/analysis/modules/assignAndCompare.js
@@ -0,0 +1,28 @@
+var name = 'Result not used: '
+var desc = 'The result of an operation was not used.'
+var categories = require('./categories')
+var common = require('./staticAnalysisCommon')
+
+function assignAndCompare () {
+ this.warningNodes = []
+}
+
+assignAndCompare.prototype.visit = function (node) {
+ if (common.isSubScopeWithTopLevelUnAssignedBinOp(node)) common.getUnAssignedTopLevelBinOps(node).forEach((n) => this.warningNodes.push(n))
+}
+
+assignAndCompare.prototype.report = function (compilationResults) {
+ return this.warningNodes.map(function (item, i) {
+ return {
+ warning: 'A binary operation yields a value that is not used in the following. This is often caused by confusing assignment (=) and comparison (==).',
+ location: item.src
+ }
+ })
+}
+
+module.exports = {
+ name: name,
+ description: desc,
+ category: categories.MISC,
+ Module: assignAndCompare
+}
diff --git a/remix-analyzer/src/analysis/modules/blockBlockhash.js b/remix-analyzer/src/analysis/modules/blockBlockhash.js
new file mode 100644
index 0000000000..094c2d33d4
--- /dev/null
+++ b/remix-analyzer/src/analysis/modules/blockBlockhash.js
@@ -0,0 +1,32 @@
+var name = 'Block.blockhash usage: '
+var desc = 'Semantics maybe unclear'
+var categories = require('./categories')
+var common = require('./staticAnalysisCommon')
+
+function blockBlockhash () {
+ this.warningNodes = []
+}
+
+blockBlockhash.prototype.visit = function (node) {
+ if (common.isBlockBlockHashAccess(node)) this.warningNodes.push(node)
+}
+
+blockBlockhash.prototype.report = function (compilationResults) {
+ return this.warningNodes.map(function (item, i) {
+ return {
+ warning: `use of "block.blockhash": "block.blockhash" is used to access the last 256 block hashes.
+ A miner computes the block hash by "summing up" the information in the current block mined.
+ By "summing up" the information in a clever way a miner can try to influence the outcome of a transaction in the current block.
+ This is especially easy if there are only a small number of equally likely outcomes.`,
+ location: item.src
+ }
+ })
+}
+
+module.exports = {
+ name: name,
+ description: desc,
+ category: categories.SECURITY,
+ Module: blockBlockhash
+}
+
diff --git a/remix-analyzer/src/analysis/modules/blockTimestamp.js b/remix-analyzer/src/analysis/modules/blockTimestamp.js
new file mode 100644
index 0000000000..a91d77ff81
--- /dev/null
+++ b/remix-analyzer/src/analysis/modules/blockTimestamp.js
@@ -0,0 +1,40 @@
+var name = 'Block timestamp: '
+var desc = 'Semantics maybe unclear'
+var categories = require('./categories')
+var common = require('./staticAnalysisCommon')
+
+function blockTimestamp () {
+ this.warningNowNodes = []
+ this.warningblockTimestampNodes = []
+}
+
+blockTimestamp.prototype.visit = function (node) {
+ if (common.isNowAccess(node)) this.warningNowNodes.push(node)
+ else if (common.isBlockTimestampAccess(node)) this.warningblockTimestampNodes.push(node)
+}
+
+blockTimestamp.prototype.report = function (compilationResults) {
+ return this.warningNowNodes.map(function (item, i) {
+ return {
+ warning: `use of "now": "now" does not mean current time. Now is an alias for block.timestamp.
+ Block.timestamp can be influenced by miners to a certain degree, be careful.`,
+ location: item.src,
+ more: 'http://solidity.readthedocs.io/en/develop/frequently-asked-questions.html#are-timestamps-now-block-timestamp-reliable'
+ }
+ }).concat(this.warningblockTimestampNodes.map(function (item, i) {
+ return {
+ warning: `use of "block.timestamp": "block.timestamp" can be influenced by miners to a certain degree.
+ That means that a miner can "choose" the block.timestamp, to a certain degree, to change the outcome of a transaction in the mined block.`,
+ location: item.src,
+ more: 'http://solidity.readthedocs.io/en/develop/frequently-asked-questions.html#are-timestamps-now-block-timestamp-reliable'
+ }
+ }))
+}
+
+module.exports = {
+ name: name,
+ description: desc,
+ category: categories.SECURITY,
+ Module: blockTimestamp
+}
+
diff --git a/remix-analyzer/src/analysis/modules/categories.js b/remix-analyzer/src/analysis/modules/categories.js
new file mode 100644
index 0000000000..2b0e8d3414
--- /dev/null
+++ b/remix-analyzer/src/analysis/modules/categories.js
@@ -0,0 +1,5 @@
+module.exports = {
+ SECURITY: {displayName: 'Security', id: 'SEC'},
+ GAS: {displayName: 'Gas & Economy', id: 'GAS'},
+ MISC: {displayName: 'Miscellaneous', id: 'MISC'}
+}
diff --git a/remix-analyzer/src/analysis/modules/checksEffectsInteraction.js b/remix-analyzer/src/analysis/modules/checksEffectsInteraction.js
new file mode 100644
index 0000000000..54a1dd051b
--- /dev/null
+++ b/remix-analyzer/src/analysis/modules/checksEffectsInteraction.js
@@ -0,0 +1,88 @@
+var name = 'Check effects: '
+var desc = 'Avoid potential reentrancy bugs'
+var categories = require('./categories')
+var common = require('./staticAnalysisCommon')
+var fcallGraph = require('./functionCallGraph')
+var AbstractAst = require('./abstractAstView')
+
+function checksEffectsInteraction () {
+ this.abstractAst = new AbstractAst()
+ this.visit = this.abstractAst.build_visit(
+ (node) => common.isInteraction(node) || common.isEffect(node) || common.isLocalCallGraphRelevantNode(node)
+ )
+
+ this.report = this.abstractAst.build_report(report)
+}
+
+checksEffectsInteraction.prototype.visit = function () { throw new Error('checksEffectsInteraction.js no visit function set upon construction') }
+
+checksEffectsInteraction.prototype.report = function () { throw new Error('checksEffectsInteraction.js no report function set upon construction') }
+
+function report (contracts, multipleContractsWithSameName) {
+ var warnings = []
+ var hasModifiers = contracts.some((item) => item.modifiers.length > 0)
+
+ var callGraph = fcallGraph.buildGlobalFuncCallGraph(contracts)
+
+ contracts.forEach((contract) => {
+ contract.functions.forEach((func) => {
+ func.changesState = checkIfChangesState(common.getFullQuallyfiedFuncDefinitionIdent(contract.node, func.node, func.parameters),
+ getContext(callGraph, contract, func))
+ })
+
+ contract.functions.forEach((func) => {
+ if (isPotentialVulnerableFunction(func, getContext(callGraph, contract, func))) {
+ var funcName = common.getFullQuallyfiedFuncDefinitionIdent(contract.node, func.node, func.parameters)
+ var comments = (hasModifiers) ? 'Note: Modifiers are currently not considered by this static analysis.' : ''
+ comments += (multipleContractsWithSameName) ? 'Note: Import aliases are currently not supported by this static analysis.' : ''
+ warnings.push({
+ warning: `Potential Violation of Checks-Effects-Interaction pattern in ${funcName}: Could potentially lead to re-entrancy vulnerability. ${comments}`,
+ location: func.src,
+ more: 'http://solidity.readthedocs.io/en/develop/security-considerations.html#re-entrancy'
+ })
+ }
+ })
+ })
+
+ return warnings
+}
+
+function getContext (callGraph, currentContract, func) {
+ return { callGraph: callGraph, currentContract: currentContract, stateVariables: getStateVariables(currentContract, func) }
+}
+
+function getStateVariables (contract, func) {
+ return contract.stateVariables.concat(func.localVariables.filter(common.isStorageVariableDeclaration))
+}
+
+function isPotentialVulnerableFunction (func, context) {
+ var isPotentialVulnerable = false
+ var interaction = false
+ func.relevantNodes.forEach((node) => {
+ if (common.isInteraction(node)) {
+ interaction = true
+ } else if (interaction && (common.isWriteOnStateVariable(node, context.stateVariables) || isLocalCallWithStateChange(node, context))) {
+ isPotentialVulnerable = true
+ }
+ })
+ return isPotentialVulnerable
+}
+
+function isLocalCallWithStateChange (node, context) {
+ if (common.isLocalCallGraphRelevantNode(node)) {
+ var func = fcallGraph.resolveCallGraphSymbol(context.callGraph, common.getFullQualifiedFunctionCallIdent(context.currentContract.node, node))
+ return !func || (func && func.node.changesState)
+ }
+ return false
+}
+
+function checkIfChangesState (startFuncName, context) {
+ return fcallGraph.analyseCallGraph(context.callGraph, startFuncName, context, (node, context) => common.isWriteOnStateVariable(node, context.stateVariables))
+}
+
+module.exports = {
+ name: name,
+ description: desc,
+ category: categories.SECURITY,
+ Module: checksEffectsInteraction
+}
diff --git a/remix-analyzer/src/analysis/modules/constantFunctions.js b/remix-analyzer/src/analysis/modules/constantFunctions.js
new file mode 100644
index 0000000000..5da3562eb6
--- /dev/null
+++ b/remix-analyzer/src/analysis/modules/constantFunctions.js
@@ -0,0 +1,108 @@
+var name = 'Constant functions: '
+var desc = 'Check for potentially constant functions'
+var categories = require('./categories')
+var common = require('./staticAnalysisCommon')
+var fcallGraph = require('./functionCallGraph')
+var AbstractAst = require('./abstractAstView')
+
+function constantFunctions () {
+ this.abstractAst = new AbstractAst()
+
+ this.visit = this.abstractAst.build_visit(
+ (node) => common.isLowLevelCall(node) ||
+ common.isTransfer(node) ||
+ common.isExternalDirectCall(node) ||
+ common.isEffect(node) ||
+ common.isLocalCallGraphRelevantNode(node) ||
+ common.isInlineAssembly(node) ||
+ common.isNewExpression(node) ||
+ common.isSelfdestructCall(node) ||
+ common.isDeleteUnaryOperation(node)
+ )
+
+ this.report = this.abstractAst.build_report(report)
+}
+
+constantFunctions.prototype.visit = function () { throw new Error('constantFunctions.js no visit function set upon construction') }
+
+constantFunctions.prototype.report = function () { throw new Error('constantFunctions.js no report function set upon construction') }
+
+function report (contracts, multipleContractsWithSameName) {
+ var warnings = []
+ var hasModifiers = contracts.some((item) => item.modifiers.length > 0)
+
+ var callGraph = fcallGraph.buildGlobalFuncCallGraph(contracts)
+
+ contracts.forEach((contract) => {
+ contract.functions.forEach((func) => {
+ if (common.isPayableFunction(func.node) || common.isConstructor(func.node)) {
+ func.potentiallyshouldBeConst = false
+ } else {
+ func.potentiallyshouldBeConst = checkIfShouldBeConstant(common.getFullQuallyfiedFuncDefinitionIdent(contract.node, func.node, func.parameters),
+ getContext(callGraph, contract, func))
+ }
+ })
+
+ contract.functions.filter((func) => common.hasFunctionBody(func.node)).forEach((func) => {
+ if (common.isConstantFunction(func.node) !== func.potentiallyshouldBeConst) {
+ var funcName = common.getFullQuallyfiedFuncDefinitionIdent(contract.node, func.node, func.parameters)
+ var comments = (hasModifiers) ? 'Note: Modifiers are currently not considered by this static analysis.' : ''
+ comments += (multipleContractsWithSameName) ? 'Note: Import aliases are currently not supported by this static analysis.' : ''
+ if (func.potentiallyshouldBeConst) {
+ warnings.push({
+ warning: `${funcName} : Potentially should be constant but is not. ${comments}`,
+ location: func.src,
+ more: 'http://solidity.readthedocs.io/en/develop/contracts.html#constant-functions'
+ })
+ } else {
+ warnings.push({
+ warning: `${funcName} : Is constant but potentially should not be. ${comments}`,
+ location: func.src,
+ more: 'http://solidity.readthedocs.io/en/develop/contracts.html#constant-functions'
+ })
+ }
+ }
+ })
+ })
+
+ return warnings
+}
+
+function getContext (callGraph, currentContract, func) {
+ return { callGraph: callGraph, currentContract: currentContract, stateVariables: getStateVariables(currentContract, func) }
+}
+
+function getStateVariables (contract, func) {
+ return contract.stateVariables.concat(func.localVariables.filter(common.isStorageVariableDeclaration))
+}
+
+function checkIfShouldBeConstant (startFuncName, context) {
+ return !fcallGraph.analyseCallGraph(context.callGraph, startFuncName, context, isConstBreaker)
+}
+
+function isConstBreaker (node, context) {
+ return common.isWriteOnStateVariable(node, context.stateVariables) ||
+ common.isLowLevelCall(node) ||
+ common.isTransfer(node) ||
+ isCallOnNonConstExternalInterfaceFunction(node, context) ||
+ common.isCallToNonConstLocalFunction(node) ||
+ common.isInlineAssembly(node) ||
+ common.isNewExpression(node) ||
+ common.isSelfdestructCall(node) ||
+ common.isDeleteUnaryOperation(node)
+}
+
+function isCallOnNonConstExternalInterfaceFunction (node, context) {
+ if (common.isExternalDirectCall(node)) {
+ var func = fcallGraph.resolveCallGraphSymbol(context.callGraph, common.getFullQualifiedFunctionCallIdent(context.currentContract, node))
+ return !func || (func && !common.isConstantFunction(func.node.node))
+ }
+ return false
+}
+
+module.exports = {
+ name: name,
+ description: desc,
+ category: categories.MISC,
+ Module: constantFunctions
+}
diff --git a/remix-analyzer/src/analysis/modules/deleteDynamicArrays.js b/remix-analyzer/src/analysis/modules/deleteDynamicArrays.js
new file mode 100644
index 0000000000..b1143d179e
--- /dev/null
+++ b/remix-analyzer/src/analysis/modules/deleteDynamicArrays.js
@@ -0,0 +1,29 @@
+var name = 'Delete on dynamic Array: '
+var desc = 'Use require and appropriately'
+var categories = require('./categories')
+var common = require('./staticAnalysisCommon')
+
+function deleteDynamicArrays () {
+ this.rel = []
+}
+
+deleteDynamicArrays.prototype.visit = function (node) {
+ if (common.isDeleteOfDynamicArray(node)) this.rel.push(node)
+}
+
+deleteDynamicArrays.prototype.report = function (compilationResults) {
+ return this.rel.map((node) => {
+ return {
+ warning: 'The “delete” operation when applied to a dynamically sized array in Solidity generates code to delete each of the elements contained. If the array is large, this operation can surpass the block gas limit and raise an OOG exception. Also nested dynamically sized objects can produce the same results.',
+ location: node.src,
+ more: 'http://solidity.readthedocs.io/en/latest/types.html?highlight=array#delete'
+ }
+ })
+}
+
+module.exports = {
+ name: name,
+ description: desc,
+ category: categories.GAS,
+ Module: deleteDynamicArrays
+}
diff --git a/remix-analyzer/src/analysis/modules/functionCallGraph.js b/remix-analyzer/src/analysis/modules/functionCallGraph.js
new file mode 100644
index 0000000000..748e2a9b43
--- /dev/null
+++ b/remix-analyzer/src/analysis/modules/functionCallGraph.js
@@ -0,0 +1,114 @@
+'use strict'
+
+var common = require('./staticAnalysisCommon')
+
+function buildLocalFuncCallGraphInternal (functions, nodeFilter, extractNodeIdent, extractFuncDefIdent) {
+ var callGraph = {}
+ functions.forEach((func) => {
+ var calls = func.relevantNodes
+ .filter(nodeFilter)
+ .map(extractNodeIdent)
+ .filter((name) => name !== extractFuncDefIdent(func)) // filter self recursive call
+
+ callGraph[extractFuncDefIdent(func)] = { node: func, calls: calls }
+ })
+
+ return callGraph
+}
+
+/**
+ * Builds a function call graph for the current contracts.
+ * Example Contract call graph:
+ *
+ * {
+ * "KingOfTheEtherThrone": {
+ * "contracts": {...}, // Contract node as defined in abstractAstView.js
+ * "functions": {
+ * "KingOfTheEtherThrone.claimThrone(string memory)": { // function in KingOfEtherThrone
+ * "node": {...}, // function node as defined in abstractAstView.js
+ * "calls": { // list of full qualified function names which are called form this function
+ * }
+ * }
+ * }
+ * },
+ * "foo": {
+ * "contract": {...}, // Contract node as definded in abstractAstView.js
+ * "functions": {} // map from full qualified function name to func node
+ * }
+ * }
+ *
+ * @contracts {list contracts} Expects as input the contract structure defined in abstractAstView.js
+ * @return {map (string -> Contract Call Graph)} returns map from contract name to contract call graph
+ */
+function buildGlobalFuncCallGraph (contracts) {
+ var callGraph = {}
+ contracts.forEach((contract) => {
+ var filterNodes = (node) => { return common.isLocalCallGraphRelevantNode(node) || common.isExternalDirectCall(node) }
+ var getNodeIdent = (node) => { return common.getFullQualifiedFunctionCallIdent(contract.node, node) }
+ var getFunDefIdent = (funcDef) => { return common.getFullQuallyfiedFuncDefinitionIdent(contract.node, funcDef.node, funcDef.parameters) }
+
+ callGraph[common.getContractName(contract.node)] = { contract: contract, functions: buildLocalFuncCallGraphInternal(contract.functions, filterNodes, getNodeIdent, getFunDefIdent) }
+ })
+
+ return callGraph
+}
+
+/**
+ * Walks through the call graph from a defined starting function, true if nodeCheck holds for every relevant node in the callgraph
+ * @callGraph {callGraph} As returned by buildGlobalFuncCallGraph
+ * @funcName {string} full qualified name of the starting function
+ * @context {Object} provides additional context information that can be used by the nodeCheck function
+ * @nodeCheck {(ASTNode, context) -> bool} applied on every relevant node in the call graph
+ * @return {bool} returns map from contract name to contract call graph
+ */
+function analyseCallGraph (callGraph, funcName, context, nodeCheck) {
+ return analyseCallGraphInternal(callGraph, funcName, context, (a, b) => a || b, nodeCheck, {})
+}
+
+function analyseCallGraphInternal (callGraph, funcName, context, combinator, nodeCheck, visited) {
+ var current = resolveCallGraphSymbol(callGraph, funcName)
+
+ if (current === undefined || visited[funcName] === true) return true
+ visited[funcName] = true
+
+ return combinator(current.node.relevantNodes.reduce((acc, val) => combinator(acc, nodeCheck(val, context)), false),
+ current.calls.reduce((acc, val) => combinator(acc, analyseCallGraphInternal(callGraph, val, context, combinator, nodeCheck, visited)), false))
+}
+
+function resolveCallGraphSymbol (callGraph, funcName) {
+ return resolveCallGraphSymbolInternal(callGraph, funcName, false)
+}
+
+function resolveCallGraphSymbolInternal (callGraph, funcName, silent) {
+ var current
+ if (funcName.includes('.')) {
+ var parts = funcName.split('.')
+ var contractPart = parts[0]
+ var functionPart = parts[1]
+ var currentContract = callGraph[contractPart]
+ if (!(currentContract === undefined)) {
+ current = currentContract.functions[funcName]
+ // resolve inheritance hierarchy
+ if (current === undefined) {
+ // resolve inheritance lookup in linearized fashion
+ var inheritsFromNames = currentContract.contract.inheritsFrom.reverse()
+ for (var i = 0; i < inheritsFromNames.length; i++) {
+ var res = resolveCallGraphSymbolInternal(callGraph, inheritsFromNames[i] + '.' + functionPart, true)
+ if (!(res === undefined)) return res
+ }
+ }
+ } else {
+ if (!silent) console.log(`static analysis functionCallGraph.js: Contract ${contractPart} not found in function call graph.`)
+ }
+ } else {
+ throw new Error('functionCallGraph.js: function does not have full qualified name.')
+ }
+ if (current === undefined && !silent) console.log(`static analysis functionCallGraph.js: ${funcName} not found in function call graph.`)
+ return current
+}
+
+module.exports = {
+ analyseCallGraph: analyseCallGraph,
+ buildGlobalFuncCallGraph: buildGlobalFuncCallGraph,
+ resolveCallGraphSymbol: resolveCallGraphSymbol
+}
diff --git a/remix-analyzer/src/analysis/modules/gasCosts.js b/remix-analyzer/src/analysis/modules/gasCosts.js
new file mode 100644
index 0000000000..d9d09cfa4c
--- /dev/null
+++ b/remix-analyzer/src/analysis/modules/gasCosts.js
@@ -0,0 +1,65 @@
+var name = 'Gas costs: '
+var desc = 'Warn if the gas requirements of functions are too high.'
+var categories = require('./categories')
+
+function gasCosts () {
+}
+
+/**
+ * call the given @arg cb (function) for all the contracts. Uses last compilation result
+ * stop visiting when cb return true
+ * @param {Function} cb - callback
+ */
+ // @TODO has been copied from remix-ide repo ! should fix that soon !
+function visitContracts (contracts, cb) {
+ for (var file in contracts) {
+ for (var name in contracts[file]) {
+ if (cb({ name: name, object: contracts[file][name], file: file })) return
+ }
+ }
+}
+
+gasCosts.prototype.report = function (compilationResults) {
+ var report = []
+ visitContracts(compilationResults.contracts, (contract) => {
+ if (
+ !contract.object.evm.gasEstimates ||
+ !contract.object.evm.gasEstimates.external
+ ) {
+ return
+ }
+ var fallback = contract.object.evm.gasEstimates.external['']
+ if (fallback !== undefined) {
+ if (fallback === null || fallback >= 2100 || fallback === 'infinite') {
+ report.push({
+ warning: `Fallback function of contract ${contract.name} requires too much gas (${fallback}).
+ If the fallback function requires more than 2300 gas, the contract cannot receive Ether.`
+ })
+ }
+ }
+
+ for (var functionName in contract.object.evm.gasEstimates.external) {
+ if (functionName === '') {
+ continue
+ }
+ var gas = contract.object.evm.gasEstimates.external[functionName]
+ var gasString = gas === null ? 'unknown or not constant' : 'high: ' + gas
+ if (gas === null || gas >= 3000000 || gas === 'infinite') {
+ report.push({
+ warning: `Gas requirement of function ${contract.name}.${functionName} ${gasString}.
+ If the gas requirement of a function is higher than the block gas limit, it cannot be executed.
+ Please avoid loops in your functions or actions that modify large areas of storage
+ (this includes clearing or copying arrays in storage)`
+ })
+ }
+ }
+ })
+ return report
+}
+
+module.exports = {
+ name: name,
+ description: desc,
+ category: categories.GAS,
+ Module: gasCosts
+}
diff --git a/remix-analyzer/src/analysis/modules/guardConditions.js b/remix-analyzer/src/analysis/modules/guardConditions.js
new file mode 100644
index 0000000000..a9d877c57c
--- /dev/null
+++ b/remix-analyzer/src/analysis/modules/guardConditions.js
@@ -0,0 +1,29 @@
+var name = 'Guard Conditions: '
+var desc = 'Use require and appropriately'
+var categories = require('./categories')
+var common = require('./staticAnalysisCommon')
+
+function guardConditions () {
+ this.guards = []
+}
+
+guardConditions.prototype.visit = function (node) {
+ if (common.isRequireCall(node) || common.isAssertCall(node)) this.guards.push(node)
+}
+
+guardConditions.prototype.report = function (compilationResults) {
+ if (this.guards.length > 0) {
+ return [{
+ warning: 'Use assert(x) if you never ever want x to be false, not in any circumstance (apart from a bug in your code). Use require(x) if x can be false, due to e.g. invalid input or a failing external component.',
+ more: 'http://solidity.readthedocs.io/en/develop/control-structures.html#error-handling-assert-require-revert-and-exceptions'
+ }]
+ }
+ return []
+}
+
+module.exports = {
+ name: name,
+ description: desc,
+ category: categories.MISC,
+ Module: guardConditions
+}
diff --git a/remix-analyzer/src/analysis/modules/inlineAssembly.js b/remix-analyzer/src/analysis/modules/inlineAssembly.js
new file mode 100644
index 0000000000..ab9b6e1d03
--- /dev/null
+++ b/remix-analyzer/src/analysis/modules/inlineAssembly.js
@@ -0,0 +1,30 @@
+var name = 'Inline assembly: '
+var desc = 'Use of Inline Assembly'
+var categories = require('./categories')
+var common = require('./staticAnalysisCommon')
+
+function inlineAssembly () {
+ this.inlineAssNodes = []
+}
+
+inlineAssembly.prototype.visit = function (node) {
+ if (common.isInlineAssembly(node)) this.inlineAssNodes.push(node)
+}
+
+inlineAssembly.prototype.report = function (compilationResults) {
+ return this.inlineAssNodes.map((node) => {
+ return {
+ warning: `CAUTION: The Contract uses inline assembly, this is only advised in rare cases.
+ Additionally static analysis modules do not parse inline Assembly, this can lead to wrong analysis results.`,
+ location: node.src,
+ more: 'http://solidity.readthedocs.io/en/develop/assembly.html#solidity-assembly'
+ }
+ })
+}
+
+module.exports = {
+ name: name,
+ description: desc,
+ category: categories.SECURITY,
+ Module: inlineAssembly
+}
diff --git a/remix-analyzer/src/analysis/modules/intDivisionTruncate.js b/remix-analyzer/src/analysis/modules/intDivisionTruncate.js
new file mode 100644
index 0000000000..51a3199a99
--- /dev/null
+++ b/remix-analyzer/src/analysis/modules/intDivisionTruncate.js
@@ -0,0 +1,28 @@
+var name = 'Data Trucated: '
+var desc = 'Division on int/uint values truncates the result.'
+var categories = require('./categories')
+var common = require('./staticAnalysisCommon')
+
+function intDivitionTruncate () {
+ this.warningNodes = []
+}
+
+intDivitionTruncate.prototype.visit = function (node) {
+ if (common.isIntDivision(node)) this.warningNodes.push(node)
+}
+
+intDivitionTruncate.prototype.report = function (compilationResults) {
+ return this.warningNodes.map(function (item, i) {
+ return {
+ warning: 'Division of integer values yields an integer value again. That means eg. a / 100 = 0 instead of 0.a since the result is an integer again. This does not hold for division of (only) literal values since those yield rational constants.',
+ location: item.src
+ }
+ })
+}
+
+module.exports = {
+ name: name,
+ description: desc,
+ category: categories.MISC,
+ Module: intDivitionTruncate
+}
diff --git a/remix-analyzer/src/analysis/modules/list.js b/remix-analyzer/src/analysis/modules/list.js
new file mode 100644
index 0000000000..f693508ac4
--- /dev/null
+++ b/remix-analyzer/src/analysis/modules/list.js
@@ -0,0 +1,17 @@
+module.exports = [
+ require('./txOrigin'),
+ require('./gasCosts'),
+ require('./thisLocal'),
+ require('./checksEffectsInteraction'),
+ require('./constantFunctions'),
+ require('./similarVariableNames.js'),
+ require('./inlineAssembly'),
+ require('./blockTimestamp'),
+ require('./lowLevelCalls'),
+ require('./blockBlockhash'),
+ require('./noReturn'),
+ require('./selfdestruct'),
+ require('./guardConditions'),
+ require('./deleteDynamicArrays'),
+ require('./assignAndCompare')
+]
diff --git a/remix-analyzer/src/analysis/modules/lowLevelCalls.js b/remix-analyzer/src/analysis/modules/lowLevelCalls.js
new file mode 100644
index 0000000000..6e8dd0a352
--- /dev/null
+++ b/remix-analyzer/src/analysis/modules/lowLevelCalls.js
@@ -0,0 +1,64 @@
+var name = 'Low level calls: '
+var desc = 'Semantics maybe unclear'
+var categories = require('./categories')
+var common = require('./staticAnalysisCommon')
+
+function lowLevelCalls () {
+ this.llcNodes = []
+}
+
+lowLevelCalls.prototype.visit = function (node) {
+ if (common.isLowLevelCallInst(node)) {
+ this.llcNodes.push({node: node, type: common.lowLevelCallTypes.CALL})
+ } else if (common.isLowLevelCallcodeInst(node)) {
+ this.llcNodes.push({node: node, type: common.lowLevelCallTypes.CALLCODE})
+ } else if (common.isLowLevelDelegatecallInst(node)) {
+ this.llcNodes.push({node: node, type: common.lowLevelCallTypes.DELEGATECALL})
+ } else if (common.isLowLevelSendInst(node)) {
+ this.llcNodes.push({node: node, type: common.lowLevelCallTypes.SEND})
+ }
+}
+
+lowLevelCalls.prototype.report = function (compilationResults) {
+ return this.llcNodes.map(function (item, i) {
+ var text = ''
+ var morehref = null
+ switch (item.type) {
+ case common.lowLevelCallTypes.CALL:
+ text = `use of "call": the use of low level "call" should be avoided whenever possible.
+ It can lead to unexpected behavior if return value is not handled properly.
+ Please use Direct Calls via specifying the called contract's interface.`
+ morehref = 'http://solidity.readthedocs.io/en/develop/control-structures.html?#external-function-calls'
+ // http://solidity.readthedocs.io/en/develop/frequently-asked-questions.html?#why-is-the-low-level-function-call-less-favorable-than-instantiating-a-contract-with-a-variable-contractb-b-and-executing-its-functions-b-dosomething
+ break
+ case common.lowLevelCallTypes.CALLCODE:
+ text = `use of "callcode": the use of low level "callcode" should be avoided whenever possible.
+ External code that is called can change the state of the calling contract and send ether from the caller's balance.
+ If this is wanted behaviour use the Solidity library feature if possible.`
+ morehref = 'http://solidity.readthedocs.io/en/develop/contracts.html#libraries'
+ break
+ case common.lowLevelCallTypes.DELEGATECALL:
+ text = `use of "delegatecall": the use of low level "delegatecall" should be avoided whenever possible.
+ External code that is called can change the state of the calling contract and send ether from the caller's balance.
+ If this is wanted behaviour use the Solidity library feature if possible.`
+ morehref = 'http://solidity.readthedocs.io/en/develop/contracts.html#libraries'
+ break
+ case common.lowLevelCallTypes.SEND:
+ text = `use of "send": "send" does not throw an exception when not successful, make sure you deal with the failure case accordingly.
+ Use "transfer" whenever failure of the ether transfer should rollback the whole transaction.
+ Note: if you "send/transfer" ether to a contract the fallback function is called, the callees fallback function is very limited due to the limited amount of gas provided by "send/transfer".
+ No state changes are possible but the callee can log the event or revert the transfer. "send/transfer" is syntactic sugar for a "call" to the fallback function with 2300 gas and a specified ether value.`
+ morehref = 'http://solidity.readthedocs.io/en/develop/security-considerations.html#sending-and-receiving-ether'
+ break
+ }
+ return { warning: text, more: morehref, location: item.node.src }
+ })
+}
+
+module.exports = {
+ name: name,
+ description: desc,
+ category: categories.SECURITY,
+ Module: lowLevelCalls
+}
+
diff --git a/remix-analyzer/src/analysis/modules/noReturn.js b/remix-analyzer/src/analysis/modules/noReturn.js
new file mode 100644
index 0000000000..dbd212ead5
--- /dev/null
+++ b/remix-analyzer/src/analysis/modules/noReturn.js
@@ -0,0 +1,73 @@
+var name = 'no return: '
+var desc = 'Function with return type is not returning'
+var categories = require('./categories')
+var common = require('./staticAnalysisCommon')
+var AbstractAst = require('./abstractAstView')
+
+function noReturn () {
+ this.abstractAst = new AbstractAst()
+
+ this.visit = this.abstractAst.build_visit(
+ (node) => common.isReturn(node) || common.isAssignment(node)
+ )
+
+ this.report = this.abstractAst.build_report(report)
+}
+
+noReturn.prototype.visit = function () { throw new Error('noReturn.js no visit function set upon construction') }
+
+noReturn.prototype.report = function () { throw new Error('noReturn.js no report function set upon construction') }
+
+function report (contracts, multipleContractsWithSameName) {
+ var warnings = []
+
+ contracts.forEach((contract) => {
+ contract.functions.filter((func) => common.hasFunctionBody(func.node)).forEach((func) => {
+ var funcName = common.getFullQuallyfiedFuncDefinitionIdent(contract.node, func.node, func.parameters)
+ if (hasNamedAndUnnamedReturns(func)) {
+ warnings.push({
+ warning: `${funcName}: Mixing of named and unnamed return parameters is not advised.`,
+ location: func.src
+ })
+ } else if (shouldReturn(func) && !(hasReturnStatement(func) || (hasNamedReturns(func) && hasAssignToAllNamedReturns(func)))) {
+ warnings.push({
+ warning: `${funcName}: Defines a return type but never explicitly returns a value.`,
+ location: func.src
+ })
+ }
+ })
+ })
+
+ return warnings
+}
+
+function shouldReturn (func) {
+ return func.returns.length > 0
+}
+
+function hasReturnStatement (func) {
+ return func.relevantNodes.filter(common.isReturn).length > 0
+}
+
+function hasAssignToAllNamedReturns (func) {
+ var namedReturns = func.returns.filter((n) => n.name.length > 0).map((n) => n.name)
+ var assignedVars = func.relevantNodes.filter(common.isAssignment).map(common.getEffectedVariableName)
+ var diff = namedReturns.filter(e => !assignedVars.includes(e))
+ return diff.length === 0
+}
+
+function hasNamedReturns (func) {
+ return func.returns.filter((n) => n.name.length > 0).length > 0
+}
+
+function hasNamedAndUnnamedReturns (func) {
+ return func.returns.filter((n) => n.name.length === 0).length > 0 &&
+ hasNamedReturns(func)
+}
+
+module.exports = {
+ name: name,
+ description: desc,
+ category: categories.MISC,
+ Module: noReturn
+}
diff --git a/remix-analyzer/src/analysis/modules/selfdestruct.js b/remix-analyzer/src/analysis/modules/selfdestruct.js
new file mode 100644
index 0000000000..14b24f9e3c
--- /dev/null
+++ b/remix-analyzer/src/analysis/modules/selfdestruct.js
@@ -0,0 +1,31 @@
+var name = 'Selfdestruct: '
+var desc = 'Be aware of caller contracts.'
+var categories = require('./categories')
+var common = require('./staticAnalysisCommon')
+
+function selfdestruct () {
+ this.relevantNodes = []
+}
+
+selfdestruct.prototype.visit = function (node) {
+ if (common.isSelfdestructCall(node)) {
+ this.relevantNodes.push(node)
+ }
+}
+
+selfdestruct.prototype.report = function () {
+ return this.relevantNodes.map(function (item, i) {
+ return {
+ warning: 'Use of selfdestruct: can block calling contracts unexpectedly. Be especially careful if this contract is planned to be used by other contracts (i.e. library contracts, interactions). Selfdestruction of the callee contract can leave callers in an inoperable state.',
+ location: item.src,
+ more: 'https://paritytech.io/blog/security-alert.html'
+ }
+ })
+}
+
+module.exports = {
+ name: name,
+ description: desc,
+ category: categories.SECURITY,
+ Module: selfdestruct
+}
diff --git a/remix-analyzer/src/analysis/modules/similarVariableNames.js b/remix-analyzer/src/analysis/modules/similarVariableNames.js
new file mode 100644
index 0000000000..903fbab20d
--- /dev/null
+++ b/remix-analyzer/src/analysis/modules/similarVariableNames.js
@@ -0,0 +1,86 @@
+var name = 'Similar variable names: '
+var desc = 'Check if variable names are too similar'
+var categories = require('./categories')
+var common = require('./staticAnalysisCommon')
+var AbstractAst = require('./abstractAstView')
+var levenshtein = require('fast-levenshtein')
+var remixLib = require('remix-lib')
+var util = remixLib.util
+
+function similarVariableNames () {
+ this.abstractAst = new AbstractAst()
+
+ this.visit = this.abstractAst.build_visit(
+ (node) => false
+ )
+
+ this.report = this.abstractAst.build_report(report)
+}
+
+similarVariableNames.prototype.visit = function () { throw new Error('similarVariableNames.js no visit function set upon construction') }
+
+similarVariableNames.prototype.report = function () { throw new Error('similarVariableNames.js no report function set upon construction') }
+
+function report (contracts, multipleContractsWithSameName) {
+ var warnings = []
+ var hasModifiers = contracts.some((item) => item.modifiers.length > 0)
+
+ contracts.forEach((contract) => {
+ contract.functions.forEach((func) => {
+ var funcName = common.getFullQuallyfiedFuncDefinitionIdent(contract.node, func.node, func.parameters)
+ var hasModifiersComments = ''
+ if (hasModifiers) {
+ hasModifiersComments = 'Note: Modifiers are currently not considered by this static analysis.'
+ }
+ var multipleContractsWithSameNameComments = ''
+ if (multipleContractsWithSameName) {
+ multipleContractsWithSameNameComments = 'Note: Import aliases are currently not supported by this static analysis.'
+ }
+
+ var vars = getFunctionVariables(contract, func).map(common.getDeclaredVariableName)
+
+ findSimilarVarNames(vars).map((sim) => {
+ warnings.push({
+ warning: `${funcName} : Variables have very similar names ${sim.var1} and ${sim.var2}. ${hasModifiersComments} ${multipleContractsWithSameNameComments}`,
+ location: func.src
+ })
+ })
+ })
+ })
+
+ return warnings
+}
+
+function findSimilarVarNames (vars) {
+ var similar = []
+ var comb = {}
+ vars.map((varName1) => vars.map((varName2) => {
+ if (varName1.length > 1 && varName2.length > 1 && varName2 !== varName1 && !isCommonPrefixedVersion(varName1, varName2) && !isCommonNrSuffixVersion(varName1, varName2) && !(comb[varName1 + ';' + varName2] || comb[varName2 + ';' + varName1])) {
+ comb[varName1 + ';' + varName2] = true
+ var distance = levenshtein.get(varName1, varName2)
+ if (distance <= 2) similar.push({ var1: varName1, var2: varName2, distance: distance })
+ }
+ }))
+ return similar
+}
+
+function isCommonPrefixedVersion (varName1, varName2) {
+ return (varName1.startsWith('_') && varName1.slice(1) === varName2) || (varName2.startsWith('_') && varName2.slice(1) === varName1)
+}
+
+function isCommonNrSuffixVersion (varName1, varName2) {
+ var ref = '^' + util.escapeRegExp(varName1.slice(0, -1)) + '[0-9]*$'
+
+ return varName2.match(ref) != null
+}
+
+function getFunctionVariables (contract, func) {
+ return contract.stateVariables.concat(func.localVariables)
+}
+
+module.exports = {
+ name: name,
+ description: desc,
+ category: categories.MISC,
+ Module: similarVariableNames
+}
diff --git a/remix-analyzer/src/analysis/modules/staticAnalysisCommon.js b/remix-analyzer/src/analysis/modules/staticAnalysisCommon.js
new file mode 100644
index 0000000000..51929708d0
--- /dev/null
+++ b/remix-analyzer/src/analysis/modules/staticAnalysisCommon.js
@@ -0,0 +1,1028 @@
+'use strict'
+
+var remixLib = require('remix-lib')
+var util = remixLib.util
+
+var nodeTypes = {
+ IDENTIFIER: 'Identifier',
+ MEMBERACCESS: 'MemberAccess',
+ FUNCTIONCALL: 'FunctionCall',
+ FUNCTIONDEFINITION: 'FunctionDefinition',
+ VARIABLEDECLARATION: 'VariableDeclaration',
+ ASSIGNMENT: 'Assignment',
+ CONTRACTDEFINITION: 'ContractDefinition',
+ UNARYOPERATION: 'UnaryOperation',
+ BINARYOPERATION: 'BinaryOperation',
+ EXPRESSIONSTATEMENT: 'ExpressionStatement',
+ MODIFIERDEFINITION: 'ModifierDefinition',
+ MODIFIERINVOCATION: 'ModifierInvocation',
+ INHERITANCESPECIFIER: 'InheritanceSpecifier',
+ USERDEFINEDTYPENAME: 'UserDefinedTypeName',
+ INLINEASSEMBLY: 'InlineAssembly',
+ BLOCK: 'Block',
+ NEWEXPRESSION: 'NewExpression',
+ RETURN: 'Return',
+ IFSTATEMENT: 'IfStatement',
+ FORSTATEMENT: 'ForStatement',
+ WHILESTATEMENT: 'WhileStatement',
+ DOWHILESTATEMENT: 'DoWhileStatement'
+}
+
+var basicTypes = {
+ UINT: 'uint256',
+ BOOL: 'bool',
+ ADDRESS: 'address',
+ BYTES32: 'bytes32',
+ STRING_MEM: 'string memory',
+ BYTES_MEM: 'bytes memory',
+ BYTES4: 'bytes4'
+}
+
+var basicRegex = {
+ CONTRACTTYPE: '^contract ',
+ FUNCTIONTYPE: '^function \\(',
+ EXTERNALFUNCTIONTYPE: '^function \\(.*\\).* external',
+ CONSTANTFUNCTIONTYPE: '^function \\(.*\\).* (constant|view|pure)',
+ REFTYPE: '( storage )|(mapping\\()|(\\[\\])',
+ FUNCTIONSIGNATURE: '^function \\(([^\\(]*)\\)',
+ LIBRARYTYPE: '^type\\(library (.*)\\)'
+}
+
+var basicFunctionTypes = {
+ SEND: buildFunctionSignature([basicTypes.UINT], [basicTypes.BOOL], false),
+ CALL: buildFunctionSignature([], [basicTypes.BOOL], true),
+ DELEGATECALL: buildFunctionSignature([], [basicTypes.BOOL], false),
+ TRANSFER: buildFunctionSignature([basicTypes.UINT], [], false)
+}
+
+var builtinFunctions = {
+ 'keccak256()': true,
+ 'sha3()': true,
+ 'sha256()': true,
+ 'ripemd160()': true,
+ 'ecrecover(bytes32,uint8,bytes32,bytes32)': true,
+ 'addmod(uint256,uint256,uint256)': true,
+ 'mulmod(uint256,uint256,uint256)': true,
+ 'selfdestruct(address)': true,
+ 'revert()': true,
+ 'revert(string memory)': true,
+ 'assert(bool)': true,
+ 'require(bool)': true,
+ 'require(bool,string memory)': true,
+ 'gasleft()': true,
+ 'blockhash(uint)': true
+}
+
+var lowLevelCallTypes = {
+ CALL: { ident: 'call', type: basicFunctionTypes.CALL },
+ CALLCODE: { ident: 'callcode', type: basicFunctionTypes.CALL },
+ DELEGATECALL: { ident: 'delegatecall', type: basicFunctionTypes.DELEGATECALL },
+ SEND: { ident: 'send', type: basicFunctionTypes.SEND },
+ TRANSFER: { ident: 'transfer', type: basicFunctionTypes.TRANSFER }
+}
+
+var specialVariables = {
+ BLOCKTIMESTAMP: { obj: 'block', member: 'timestamp', type: basicTypes.UINT },
+ BLOCKHASH: {
+ obj: 'block',
+ member: 'blockhash',
+ type: buildFunctionSignature([basicTypes.UINT], [basicTypes.BYTES32], false)
+ }
+}
+
+var abiNamespace = {
+ ENCODE: {
+ obj: 'abi',
+ member: 'encode',
+ type: buildFunctionSignature([], [basicTypes.BYTES_MEM], false, 'pure')
+ },
+ ENCODEPACKED: {
+ obj: 'abi',
+ member: 'encodePacked',
+ type: buildFunctionSignature([], [basicTypes.BYTES_MEM], false, 'pure')
+ },
+ ENCODE_SELECT: {
+ obj: 'abi',
+ member: 'encodeWithSelector',
+ type: buildFunctionSignature([basicTypes.BYTES4], [basicTypes.BYTES_MEM], false, 'pure')
+ },
+ ENCODE_SIG: {
+ obj: 'abi',
+ member: 'encodeWithSignature',
+ type: buildFunctionSignature([basicTypes.STRING_MEM], [basicTypes.BYTES_MEM], false, 'pure')
+ }
+}
+
+// #################### Trivial Getters
+
+function getType (node) {
+ return node.attributes.type
+}
+
+// #################### Complex Getters
+
+/**
+ * Returns the type parameter of function call AST nodes. Throws if not a function call node
+ * @func {ASTNode} Function call node
+ * @return {string} type of function call
+ */
+function getFunctionCallType (func) {
+ if (!(isExternalDirectCall(func) || isThisLocalCall(func) || isSuperLocalCall(func) || isLocalCall(func) || isLibraryCall(func))) throw new Error('staticAnalysisCommon.js: not function call Node')
+ if (isExternalDirectCall(func) || isThisLocalCall(func) || isSuperLocalCall(func) || isLibraryCall(func)) return func.attributes.type
+ return findFirstSubNodeLTR(func, exactMatch(nodeTypes.IDENTIFIER)).attributes.type
+}
+
+/**
+ * Get the variable name written to by a effect node, except for inline assembly because there is no information to find out where we write to. Trows if not a effect node or is inlineassmbly.
+ * Example: x = 10; => x
+ * @effectNode {ASTNode} Function call node
+ * @return {string} variable name written to
+ */
+function getEffectedVariableName (effectNode) {
+ if (!isEffect(effectNode) || isInlineAssembly(effectNode)) throw new Error('staticAnalysisCommon.js: not an effect Node or inline assembly')
+ return findFirstSubNodeLTR(effectNode, exactMatch(nodeTypes.IDENTIFIER)).attributes.value
+}
+
+/**
+ * Returns the identifier of a local call, Throws on wrong node.
+ * Example: f(103) => f
+ * @localCallNode {ASTNode} Function call node
+ * @return {string} name of the function called
+ */
+function getLocalCallName (localCallNode) {
+ if (!isLocalCall(localCallNode) && !isAbiNamespaceCall(localCallNode)) throw new Error('staticAnalysisCommon.js: not an local call Node')
+ return localCallNode.children[0].attributes.value
+}
+
+/**
+ * Returns the identifier of a this local call, Throws on wrong node.
+ * Example: this.f(103) => f
+ * @localCallNode {ASTNode} Function call node
+ * @return {string} name of the function called
+ */
+function getThisLocalCallName (localCallNode) {
+ if (!isThisLocalCall(localCallNode)) throw new Error('staticAnalysisCommon.js: not an this local call Node')
+ return localCallNode.attributes.value
+}
+
+/**
+ * Returns the identifier of a super local call, Throws on wrong node.
+ * Example: super.f(103) => f
+ * @localCallNode {ASTNode} Function call node
+ * @return {string} name of the function called
+ */
+function getSuperLocalCallName (localCallNode) {
+ if (!isSuperLocalCall(localCallNode)) throw new Error('staticAnalysisCommon.js: not an super local call Node')
+ return localCallNode.attributes.member_name
+}
+
+/**
+ * Returns the contract type of a external direct call, Throws on wrong node.
+ * Example:
+ * foo x = foo(0xdeadbeef...);
+ * x.f(103) => foo
+ * @extDirectCall {ASTNode} Function call node
+ * @return {string} name of the contract the function is defined in
+ */
+function getExternalDirectCallContractName (extDirectCall) {
+ if (!isExternalDirectCall(extDirectCall)) throw new Error('staticAnalysisCommon.js: not an external direct call Node')
+ return extDirectCall.children[0].attributes.type.replace(new RegExp(basicRegex.CONTRACTTYPE), '')
+}
+
+/**
+ * Returns the name of the contract of a this local call (current contract), Throws on wrong node.
+ * Example:
+ * Contract foo {
+ * ...
+ * this.f(103) => foo
+ * ...
+ * @thisLocalCall {ASTNode} Function call node
+ * @return {string} name of the contract the function is defined in
+ */
+function getThisLocalCallContractName (thisLocalCall) {
+ if (!isThisLocalCall(thisLocalCall)) throw new Error('staticAnalysisCommon.js: not an this local call Node')
+ return thisLocalCall.children[0].attributes.type.replace(new RegExp(basicRegex.CONTRACTTYPE), '')
+}
+
+/**
+ * Returns the function identifier of a external direct call, Throws on wrong node.
+ * Example:
+ * foo x = foo(0xdeadbeef...);
+ * x.f(103) => f
+ * @extDirectCall {ASTNode} Function call node
+ * @return {string} name of the function called
+ */
+function getExternalDirectCallMemberName (extDirectCall) {
+ if (!isExternalDirectCall(extDirectCall)) throw new Error('staticAnalysisCommon.js: not an external direct call Node')
+ return extDirectCall.attributes.member_name
+}
+
+/**
+ * Returns the name of a contract, Throws on wrong node.
+ * Example:
+ * Contract foo { => foo
+ * @contract {ASTNode} Contract Definition node
+ * @return {string} name of a contract defined
+ */
+function getContractName (contract) {
+ if (!isContractDefinition(contract)) throw new Error('staticAnalysisCommon.js: not an contractDefinition Node')
+ return contract.attributes.name
+}
+
+/**
+ * Returns the name of a function definition, Throws on wrong node.
+ * Example:
+ * func foo(uint bla) { => foo
+ * @funcDef {ASTNode} Function Definition node
+ * @return {string} name of a function defined
+ */
+function getFunctionDefinitionName (funcDef) {
+ if (!isFunctionDefinition(funcDef)) throw new Error('staticAnalysisCommon.js: not an functionDefinition Node')
+ return funcDef.attributes.name
+}
+
+/**
+ * Returns the identifier of an inheritance specifier, Throws on wrong node.
+ * Example:
+ * contract KingOfTheEtherThrone is b { => b
+ * @func {ASTNode} Inheritance specifier
+ * @return {string} name of contract inherited from
+ */
+function getInheritsFromName (inheritsNode) {
+ if (!isInheritanceSpecifier(inheritsNode)) throw new Error('staticAnalysisCommon.js: not an InheritanceSpecifier Node')
+ return inheritsNode.children[0].attributes.name
+}
+
+/**
+ * Returns the identifier of a variable definition, Throws on wrong node.
+ * Example:
+ * var x = 10; => x
+ * @varDeclNode {ASTNode} Variable declaration node
+ * @return {string} variable name
+ */
+function getDeclaredVariableName (varDeclNode) {
+ if (!isVariableDeclaration(varDeclNode)) throw new Error('staticAnalysisCommon.js: not a variable declaration')
+ return varDeclNode.attributes.name
+}
+
+/**
+ * Returns state variable declaration nodes for a contract, Throws on wrong node.
+ * Example:
+ * contract foo {
+ * ...
+ * var y = true;
+ * var x = 10; => [y,x]
+ * @contractNode {ASTNode} Contract Definition node
+ * @return {list variable declaration} state variable node list
+ */
+function getStateVariableDeclarationsFormContractNode (contractNode) {
+ if (!isContractDefinition(contractNode)) throw new Error('staticAnalysisCommon.js: not a contract definition declaration')
+ if (!contractNode.children) return []
+ return contractNode.children.filter((el) => isVariableDeclaration(el))
+}
+
+/**
+ * Returns parameter node for a function or modifier definition, Throws on wrong node.
+ * Example:
+ * function bar(uint a, uint b) => uint a, uint b
+ * @funcNode {ASTNode} Contract Definition node
+ * @return {parameterlist node} parameterlist node
+ */
+function getFunctionOrModifierDefinitionParameterPart (funcNode) {
+ if (!isFunctionDefinition(funcNode) && !isModifierDefinition(funcNode)) throw new Error('staticAnalysisCommon.js: not a function definition')
+ return funcNode.children[0]
+}
+
+/**
+ * Returns return parameter node for a function or modifier definition, Throws on wrong node.
+ * Example:
+ * function bar(uint a, uint b) returns (bool a, bool b) => bool a, bool b
+ * @funcNode {ASTNode} Contract Definition node
+ * @return {parameterlist node} parameterlist node
+ */
+function getFunctionOrModifierDefinitionReturnParameterPart (funcNode) {
+ if (!isFunctionDefinition(funcNode) && !isModifierDefinition(funcNode)) throw new Error('staticAnalysisCommon.js: not a function definition')
+ return funcNode.children[1]
+}
+
+/**
+ * Extracts the parameter types for a function type signature
+ * Example:
+ * function(uint a, uint b) returns (bool) => uint a, uint b
+ * @func {ASTNode} function call node
+ * @return {string} parameter signature
+ */
+function getFunctionCallTypeParameterType (func) {
+ var type = getFunctionCallType(func)
+ if (type.startsWith('function (')) {
+ var paramTypes = ''
+ var openPar = 1
+ for (var x = 10; x < type.length; x++) {
+ var c = type.charAt(x)
+ if (c === '(') openPar++
+ else if (c === ')') openPar--
+
+ if (openPar === 0) return paramTypes
+
+ paramTypes += c
+ }
+ } else {
+ throw new Error('staticAnalysisCommon.js: cannot extract parameter types from function call')
+ }
+}
+
+/**
+ * Returns the name of the library called, Throws on wrong node.
+ * Example:
+ * library set{...}
+ * contract foo {
+ * ...
+ * function () { set.union() => set}
+ * @funcCall {ASTNode} function call node
+ * @return {string} name of the lib defined
+ */
+function getLibraryCallContractName (funcCall) {
+ if (!isLibraryCall(funcCall)) throw new Error('staticAnalysisCommon.js: not an this library call Node')
+ return new RegExp(basicRegex.LIBRARYTYPE).exec(funcCall.children[0].attributes.type)[1]
+}
+
+/**
+ * Returns the name of the function of a library call, Throws on wrong node.
+ * Example:
+ * library set{...}
+ * contract foo {
+ * ...
+ * function () { set.union() => uinion}
+ * @func {ASTNode} function call node
+ * @return {string} name of function called on the library
+ */
+function getLibraryCallMemberName (funcCall) {
+ if (!isLibraryCall(funcCall)) throw new Error('staticAnalysisCommon.js: not an library call Node')
+ return funcCall.attributes.member_name
+}
+
+/**
+ * Returns full qualified name for a function call, Throws on wrong node.
+ * Example:
+ * contract foo {
+ * ...
+ * function bar(uint b) { }
+ * function baz() {
+ * bar(10) => foo.bar(uint)
+ * @func {ASTNode} function call node
+ * @func {ASTNode} contract defintion
+ * @return {string} full qualified identifier for the function call
+ */
+function getFullQualifiedFunctionCallIdent (contract, func) {
+ if (isLocalCall(func)) return getContractName(contract) + '.' + getLocalCallName(func) + '(' + getFunctionCallTypeParameterType(func) + ')'
+ else if (isThisLocalCall(func)) return getThisLocalCallContractName(func) + '.' + getThisLocalCallName(func) + '(' + getFunctionCallTypeParameterType(func) + ')'
+ else if (isSuperLocalCall(func)) return getContractName(contract) + '.' + getSuperLocalCallName(func) + '(' + getFunctionCallTypeParameterType(func) + ')'
+ else if (isExternalDirectCall(func)) return getExternalDirectCallContractName(func) + '.' + getExternalDirectCallMemberName(func) + '(' + getFunctionCallTypeParameterType(func) + ')'
+ else if (isLibraryCall(func)) return getLibraryCallContractName(func) + '.' + getLibraryCallMemberName(func) + '(' + getFunctionCallTypeParameterType(func) + ')'
+ else throw new Error('staticAnalysisCommon.js: Can not get function name form non function call node')
+}
+
+function getFullQuallyfiedFuncDefinitionIdent (contract, func, paramTypes) {
+ return getContractName(contract) + '.' + getFunctionDefinitionName(func) + '(' + util.concatWithSeperator(paramTypes, ',') + ')'
+}
+
+function getUnAssignedTopLevelBinOps (subScope) {
+ return subScope.children.filter(isBinaryOpInExpression)
+}
+
+// #################### Trivial Node Identification
+
+function isFunctionDefinition (node) {
+ return nodeType(node, exactMatch(nodeTypes.FUNCTIONDEFINITION))
+}
+
+function isModifierDefinition (node) {
+ return nodeType(node, exactMatch(nodeTypes.MODIFIERDEFINITION))
+}
+
+function isModifierInvocation (node) {
+ return nodeType(node, exactMatch(nodeTypes.MODIFIERINVOCATION))
+}
+
+function isVariableDeclaration (node) {
+ return nodeType(node, exactMatch(nodeTypes.VARIABLEDECLARATION))
+}
+
+function isReturn (node) {
+ return nodeType(node, exactMatch(nodeTypes.RETURN))
+}
+
+function isInheritanceSpecifier (node) {
+ return nodeType(node, exactMatch(nodeTypes.INHERITANCESPECIFIER))
+}
+
+function isAssignment (node) {
+ return nodeType(node, exactMatch(nodeTypes.ASSIGNMENT))
+}
+
+function isContractDefinition (node) {
+ return nodeType(node, exactMatch(nodeTypes.CONTRACTDEFINITION))
+}
+
+function isInlineAssembly (node) {
+ return nodeType(node, exactMatch(nodeTypes.INLINEASSEMBLY))
+}
+
+function isNewExpression (node) {
+ return nodeType(node, exactMatch(nodeTypes.NEWEXPRESSION))
+}
+
+/**
+ * True if is Expression
+ * @node {ASTNode} some AstNode
+ * @return {bool}
+ */
+function isExpressionStatement (node) {
+ return nodeType(node, exactMatch(nodeTypes.EXPRESSIONSTATEMENT))
+}
+
+/**
+ * True if is binaryop
+ * @node {ASTNode} some AstNode
+ * @return {bool}
+ */
+function isBinaryOperation (node) {
+ return nodeType(node, exactMatch(nodeTypes.BINARYOPERATION))
+}
+
+// #################### Complex Node Identification
+
+/**
+ * True if function defintion has function body
+ * @funcNode {ASTNode} function defintion node
+ * @return {bool}
+ */
+function hasFunctionBody (funcNode) {
+ return findFirstSubNodeLTR(funcNode, exactMatch(nodeTypes.BLOCK)) != null
+}
+
+/**
+ * True if node is a delete instruction of a dynamic array
+ * @node {ASTNode} node to check for
+ * @return {bool}
+ */
+function isDeleteOfDynamicArray (node) {
+ return isDeleteUnaryOperation(node) && isDynamicArrayAccess(node.children[0])
+}
+
+/**
+ * True if node is node is a ref to a dynamic array
+ * @node {ASTNode} node to check for
+ * @return {bool}
+ */
+function isDynamicArrayAccess (node) {
+ return node && nodeType(node, exactMatch(nodeTypes.IDENTIFIER)) && (node.attributes.type.endsWith('[] storage ref') || node.attributes.type === 'bytes storage ref' || node.attributes.type === 'string storage ref')
+}
+
+/**
+ * True if call to code within the current contracts context including (delegate) library call
+ * @node {ASTNode} some AstNode
+ * @return {bool}
+ */
+function isLocalCallGraphRelevantNode (node) {
+ return ((isLocalCall(node) || isSuperLocalCall(node) || isLibraryCall(node)) && !isBuiltinFunctionCall(node))
+}
+
+/**
+ * True if is builtin function like assert, sha3, erecover, ...
+ * @node {ASTNode} some AstNode
+ * @return {bool}
+ */
+function isBuiltinFunctionCall (node) {
+ return (isLocalCall(node) && builtinFunctions[getLocalCallName(node) + '(' + getFunctionCallTypeParameterType(node) + ')'] === true) || isAbiNamespaceCall(node)
+}
+
+/**
+ * True if is builtin function like assert, sha3, erecover, ...
+ * @node {ASTNode} some AstNode
+ * @return {bool}
+ */
+function isAbiNamespaceCall (node) {
+ return Object.keys(abiNamespace).some((key) => abiNamespace.hasOwnProperty(key) && node.children && node.children[0] && isSpecialVariableAccess(node.children[0], abiNamespace[key]))
+}
+
+/**
+ * True if node is a call to selfdestruct
+ * @node {ASTNode} some AstNode
+ * @return {bool}
+ */
+function isSelfdestructCall (node) {
+ return isBuiltinFunctionCall(node) && getLocalCallName(node) === 'selfdestruct'
+}
+
+/**
+ * True if node is a call to builtin assert(bool)
+ * @node {ASTNode} some AstNode
+ * @return {bool}
+ */
+function isAssertCall (node) {
+ return isBuiltinFunctionCall(node) && getLocalCallName(node) === 'assert'
+}
+
+/**
+ * True if node is a call to builtin require(bool)
+ * @node {ASTNode} some AstNode
+ * @return {bool}
+ */
+function isRequireCall (node) {
+ return isBuiltinFunctionCall(node) && getLocalCallName(node) === 'require'
+}
+
+/**
+ * True if is storage variable declaration
+ * @node {ASTNode} some AstNode
+ * @return {bool}
+ */
+function isStorageVariableDeclaration (node) {
+ return isVariableDeclaration(node) && expressionType(node, basicRegex.REFTYPE)
+}
+
+/**
+ * True if is interaction with external contract (change in context, no delegate calls) (send, call of other contracts)
+ * @node {ASTNode} some AstNode
+ * @return {bool}
+ */
+function isInteraction (node) {
+ return isLLCall(node) || isLLSend(node) || isExternalDirectCall(node) || isTransfer(node)
+}
+
+/**
+ * True if node changes state of a variable or is inline assembly (does not include check if it is a global state change, on a state variable)
+ * @node {ASTNode} some AstNode
+ * @return {bool}
+ */
+function isEffect (node) {
+ return isAssignment(node) || isPlusPlusUnaryOperation(node) || isMinusMinusUnaryOperation(node) || isInlineAssembly(node)
+}
+
+/**
+ * True if node changes state of a variable or is inline assembly (Checks if variable is a state variable via provided list)
+ * @node {ASTNode} some AstNode
+ * @node {list Variable declaration} state variable declaration currently in scope
+ * @return {bool}
+ */
+function isWriteOnStateVariable (effectNode, stateVariables) {
+ return isInlineAssembly(effectNode) || (isEffect(effectNode) && isStateVariable(getEffectedVariableName(effectNode), stateVariables))
+}
+
+/**
+ * True if there is a variable with name, name in stateVariables
+ * @node {ASTNode} some AstNode
+ * @node {list Variable declaration} state variable declaration currently in scope
+ * @return {bool}
+ */
+function isStateVariable (name, stateVariables) {
+ return stateVariables.some((item) => name === getDeclaredVariableName(item))
+}
+
+/**
+ * True if is function defintion that is flaged as constant
+ * @node {ASTNode} some AstNode
+ * @return {bool}
+ */
+function isConstantFunction (node) {
+ return isFunctionDefinition(node) && (
+ node.attributes.constant === true ||
+ node.attributes.stateMutability === 'view' ||
+ node.attributes.stateMutability === 'pure'
+ )
+}
+
+/**
+ * True if is function defintion has payable modifier
+ * @node {ASTNode} some AstNode
+ * @return {bool}
+ */
+function isPayableFunction (node) {
+ return isFunctionDefinition(node) && (
+ node.attributes.stateMutability === 'payable'
+ )
+}
+
+/**
+ * True if is constructor
+ * @node {ASTNode} some AstNode
+ * @return {bool}
+ */
+function isConstructor (node) {
+ return isFunctionDefinition(node) && (
+ node.attributes.isConstructor === true
+ )
+}
+
+/**
+ * True if node is integer division that truncates (not only int literals since those yield a rational value)
+ * @node {ASTNode} some AstNode
+ * @return {bool}
+ */
+function isIntDivision (node) {
+ return isBinaryOperation(node) && operator(node, exactMatch(util.escapeRegExp('/'))) && expressionType(node, util.escapeRegExp('int'))
+}
+
+/**
+ * True if is block / SubScope has top level binops (e.g. that are not assigned to anything, most of the time confused compare instead of assign)
+ * @node {ASTNode} some AstNode
+ * @return {bool}
+ */
+function isSubScopeWithTopLevelUnAssignedBinOp (node) {
+ return nodeType(node, exactMatch(nodeTypes.BLOCK)) && node.children && node.children.some(isBinaryOpInExpression) ||
+ isSubScopeStatement(node) && node.children && node.children.some(isBinaryOpInExpression) // Second Case for if without braces
+}
+
+function isSubScopeStatement (node) {
+ return (nodeType(node, exactMatch(nodeTypes.IFSTATEMENT)) ||
+ nodeType(node, exactMatch(nodeTypes.FORSTATEMENT)) ||
+ nodeType(node, exactMatch(nodeTypes.WHILESTATEMENT)) ||
+ nodeType(node, exactMatch(nodeTypes.DOWHILESTATEMENT))) &&
+ minNrOfChildren(node, 2) && !nodeType(node.children[1], exactMatch(nodeTypes.BLOCK))
+}
+
+/**
+ * True if binary operation inside of expression statement
+ * @node {ASTNode} some AstNode
+ * @return {bool}
+ */
+function isBinaryOpInExpression (node) {
+ return isExpressionStatement(node) && nrOfChildren(node, 1) && isBinaryOperation(node.children[0])
+}
+
+/**
+ * True if unary increment operation
+ * @node {ASTNode} some AstNode
+ * @return {bool}
+ */
+function isPlusPlusUnaryOperation (node) {
+ return nodeType(node, exactMatch(nodeTypes.UNARYOPERATION)) && operator(node, exactMatch(util.escapeRegExp('++')))
+}
+
+/**
+ * True if unary delete operation
+ * @node {ASTNode} some AstNode
+ * @return {bool}
+ */
+function isDeleteUnaryOperation (node) {
+ return nodeType(node, exactMatch(nodeTypes.UNARYOPERATION)) && operator(node, exactMatch(util.escapeRegExp('delete')))
+}
+
+/**
+ * True if unary decrement operation
+ * @node {ASTNode} some AstNode
+ * @return {bool}
+ */
+function isMinusMinusUnaryOperation (node) {
+ return nodeType(node, exactMatch(nodeTypes.UNARYOPERATION)) && operator(node, exactMatch(util.escapeRegExp('--')))
+}
+
+/**
+ * True if all functions on a contract are implemented
+ * @node {ASTNode} some AstNode
+ * @return {bool}
+ */
+function isFullyImplementedContract (node) {
+ return nodeType(node, exactMatch(nodeTypes.CONTRACTDEFINITION)) && node.attributes.fullyImplemented === true
+}
+
+/**
+ * True if it is a library contract defintion
+ * @node {ASTNode} some AstNode
+ * @return {bool}
+ */
+function isLibrary (node) {
+ return nodeType(node, exactMatch(nodeTypes.CONTRACTDEFINITION)) && node.attributes.isLibrary === true
+}
+
+/**
+ * True if it is a local call to non const function
+ * @node {ASTNode} some AstNode
+ * @return {bool}
+ */
+function isCallToNonConstLocalFunction (node) {
+ return isLocalCall(node) && !expressionType(node.children[0], basicRegex.CONSTANTFUNCTIONTYPE)
+}
+
+/**
+ * True if it is a call to a library
+ * @node {ASTNode} some AstNode
+ * @return {bool}
+ */
+function isLibraryCall (node) {
+ return isMemberAccess(node, basicRegex.FUNCTIONTYPE, undefined, basicRegex.LIBRARYTYPE, undefined)
+}
+
+/**
+ * True if it is an external call via defined interface (not low level call)
+ * @node {ASTNode} some AstNode
+ * @return {bool}
+ */
+function isExternalDirectCall (node) {
+ return isMemberAccess(node, basicRegex.EXTERNALFUNCTIONTYPE, undefined, basicRegex.CONTRACTTYPE, undefined) && !isThisLocalCall(node) && !isSuperLocalCall(node)
+}
+
+/**
+ * True if access to block.timestamp via now alias
+ * @node {ASTNode} some AstNode
+ * @return {bool}
+ */
+function isNowAccess (node) {
+ return nodeType(node, exactMatch(nodeTypes.IDENTIFIER)) &&
+ expressionType(node, exactMatch(basicTypes.UINT)) &&
+ name(node, exactMatch('now'))
+}
+
+/**
+ * True if access to block.timestamp
+ * @node {ASTNode} some AstNode
+ * @return {bool}
+ */
+function isBlockTimestampAccess (node) {
+ return isSpecialVariableAccess(node, specialVariables.BLOCKTIMESTAMP)
+}
+
+/**
+ * True if access to block.blockhash
+ * @node {ASTNode} some AstNode
+ * @return {bool}
+ */
+function isBlockBlockHashAccess (node) {
+ return isSpecialVariableAccess(node, specialVariables.BLOCKHASH)
+}
+
+/**
+ * True if call to local function via this keyword
+ * @node {ASTNode} some AstNode
+ * @return {bool}
+ */
+function isThisLocalCall (node) {
+ return isMemberAccess(node, basicRegex.FUNCTIONTYPE, exactMatch('this'), basicRegex.CONTRACTTYPE, undefined)
+}
+
+/**
+ * True if access to local function via super keyword
+ * @node {ASTNode} some AstNode
+ * @return {bool}
+ */
+function isSuperLocalCall (node) {
+ return isMemberAccess(node, basicRegex.FUNCTIONTYPE, exactMatch('super'), basicRegex.CONTRACTTYPE, undefined)
+}
+
+/**
+ * True if call to local function
+ * @node {ASTNode} some AstNode
+ * @return {bool}
+ */
+function isLocalCall (node) {
+ return nodeType(node, exactMatch(nodeTypes.FUNCTIONCALL)) &&
+ minNrOfChildren(node, 1) &&
+ nodeType(node.children[0], exactMatch(nodeTypes.IDENTIFIER)) &&
+ expressionType(node.children[0], basicRegex.FUNCTIONTYPE) &&
+ !expressionType(node.children[0], basicRegex.EXTERNALFUNCTIONTYPE) &&
+ nrOfChildren(node.children[0], 0)
+}
+
+/**
+ * True if low level call (send, call, delegatecall, callcode)
+ * @node {ASTNode} some AstNode
+ * @return {bool}
+ */
+function isLowLevelCall (node) {
+ return isLLCall(node) ||
+ isLLCallcode(node) ||
+ isLLDelegatecall(node) ||
+ isLLSend(node)
+}
+
+/**
+ * True if low level send
+ * @node {ASTNode} some AstNode
+ * @return {bool}
+ */
+function isLLSend (node) {
+ return isMemberAccess(node,
+ exactMatch(util.escapeRegExp(lowLevelCallTypes.SEND.type)),
+ undefined, exactMatch(basicTypes.ADDRESS), exactMatch(lowLevelCallTypes.SEND.ident))
+}
+
+/**
+ * True if low level call
+ * @node {ASTNode} some AstNode
+ * @return {bool}
+ */
+function isLLCall (node) {
+ return isMemberAccess(node,
+ exactMatch(util.escapeRegExp(lowLevelCallTypes.CALL.type)),
+ undefined, exactMatch(basicTypes.ADDRESS), exactMatch(lowLevelCallTypes.CALL.ident))
+}
+
+/**
+ * True if low level callcode
+ * @node {ASTNode} some AstNode
+ * @return {bool}
+ */
+function isLLCallcode (node) {
+ return isMemberAccess(node,
+ exactMatch(util.escapeRegExp(lowLevelCallTypes.CALLCODE.type)),
+ undefined, exactMatch(basicTypes.ADDRESS), exactMatch(lowLevelCallTypes.CALLCODE.ident))
+}
+
+/**
+ * True if low level delegatecall
+ * @node {ASTNode} some AstNode
+ * @return {bool}
+ */
+function isLLDelegatecall (node) {
+ return isMemberAccess(node,
+ exactMatch(util.escapeRegExp(lowLevelCallTypes.DELEGATECALL.type)),
+ undefined, exactMatch(basicTypes.ADDRESS), exactMatch(lowLevelCallTypes.DELEGATECALL.ident))
+}
+
+/**
+ * True if transfer call
+ * @node {ASTNode} some AstNode
+ * @return {bool}
+ */
+function isTransfer (node) {
+ return isMemberAccess(node,
+ exactMatch(util.escapeRegExp(lowLevelCallTypes.TRANSFER.type)),
+ undefined, exactMatch(basicTypes.ADDRESS), exactMatch(lowLevelCallTypes.TRANSFER.ident))
+}
+
+// #################### Complex Node Identification - Private
+
+function isMemberAccess (node, retType, accessor, accessorType, memberName) {
+ return nodeType(node, exactMatch(nodeTypes.MEMBERACCESS)) &&
+ expressionType(node, retType) &&
+ name(node, memberName) &&
+ nrOfChildren(node, 1) &&
+ name(node.children[0], accessor) &&
+ expressionType(node.children[0], accessorType)
+}
+
+function isSpecialVariableAccess (node, varType) {
+ return isMemberAccess(node, exactMatch(util.escapeRegExp(varType.type)), varType.obj, varType.obj, varType.member)
+}
+
+// #################### Node Identification Primitives
+
+function nrOfChildren (node, nr) {
+ return (node && (nr === undefined || nr === null)) || (node && nr === 0 && !node.children) || (node && node.children && node.children.length === nr)
+}
+
+function minNrOfChildren (node, nr) {
+ return (node && (nr === undefined || nr === null)) || (node && nr === 0 && !node.children) || (node && node.children && node.children.length >= nr)
+}
+
+function expressionType (node, typeRegex) {
+ return (node && !typeRegex) || (node && node.attributes && new RegExp(typeRegex).test(node.attributes.type))
+}
+
+function nodeType (node, typeRegex) {
+ return (node && !typeRegex) || (node && new RegExp(typeRegex).test(node.name))
+}
+
+function name (node, nameRegex) {
+ var regex = new RegExp(nameRegex)
+ return (node && !nameRegex) || (node && node.attributes && (regex.test(node.attributes.value) || regex.test(node.attributes.member_name)))
+}
+
+function operator (node, opRegex) {
+ return (node && !opRegex) || (node && new RegExp(opRegex).test(node.attributes.operator))
+}
+
+// #################### Helpers
+
+function exactMatch (regexStr) {
+ return '^' + regexStr + '$'
+}
+
+/**
+ * Builds an function signature as used in the AST of the solc-json AST
+ * @param {Array} paramTypes
+ * list of parameter type names
+ * @param {Array} returnTypes
+ * list of return type names
+ * @return {Boolean} isPayable
+ */
+function buildFunctionSignature (paramTypes, returnTypes, isPayable, additionalMods) {
+ return 'function (' + util.concatWithSeperator(paramTypes, ',') + ')' + ((isPayable) ? ' payable' : '') + ((additionalMods) ? ' ' + additionalMods : '') + ((returnTypes.length) ? ' returns (' + util.concatWithSeperator(returnTypes, ',') + ')' : '')
+}
+
+/**
+ * Finds first node of a certain type under a specific node.
+ * @node {AstNode} node to start form
+ * @type {String} Type the ast node should have
+ * @return {AstNode} null or node found
+ */
+function findFirstSubNodeLTR (node, type) {
+ if (!node || !node.children) return null
+ for (let i = 0; i < node.children.length; ++i) {
+ var item = node.children[i]
+ if (nodeType(item, type)) return item
+ else {
+ var res = findFirstSubNodeLTR(item, type)
+ if (res) return res
+ }
+ }
+ return null
+}
+
+module.exports = {
+ // #################### Trivial Getters
+ getType: getType,
+ // #################### Complex Getters
+ getThisLocalCallName: getThisLocalCallName,
+ getSuperLocalCallName: getSuperLocalCallName,
+ getFunctionCallType: getFunctionCallType,
+ getContractName: getContractName,
+ getEffectedVariableName: getEffectedVariableName,
+ getDeclaredVariableName: getDeclaredVariableName,
+ getLocalCallName: getLocalCallName,
+ getInheritsFromName: getInheritsFromName,
+ getExternalDirectCallContractName: getExternalDirectCallContractName,
+ getThisLocalCallContractName: getThisLocalCallContractName,
+ getExternalDirectCallMemberName: getExternalDirectCallMemberName,
+ getFunctionDefinitionName: getFunctionDefinitionName,
+ getFunctionCallTypeParameterType: getFunctionCallTypeParameterType,
+ getLibraryCallContractName: getLibraryCallContractName,
+ getLibraryCallMemberName: getLibraryCallMemberName,
+ getFullQualifiedFunctionCallIdent: getFullQualifiedFunctionCallIdent,
+ getFullQuallyfiedFuncDefinitionIdent: getFullQuallyfiedFuncDefinitionIdent,
+ getStateVariableDeclarationsFormContractNode: getStateVariableDeclarationsFormContractNode,
+ getFunctionOrModifierDefinitionParameterPart: getFunctionOrModifierDefinitionParameterPart,
+ getFunctionOrModifierDefinitionReturnParameterPart: getFunctionOrModifierDefinitionReturnParameterPart,
+ getUnAssignedTopLevelBinOps: getUnAssignedTopLevelBinOps,
+
+ // #################### Complex Node Identification
+ isDeleteOfDynamicArray: isDeleteOfDynamicArray,
+ isAbiNamespaceCall: isAbiNamespaceCall,
+ isSpecialVariableAccess: isSpecialVariableAccess,
+ isDynamicArrayAccess: isDynamicArrayAccess,
+ isSubScopeWithTopLevelUnAssignedBinOp: isSubScopeWithTopLevelUnAssignedBinOp,
+ hasFunctionBody: hasFunctionBody,
+ isInteraction: isInteraction,
+ isEffect: isEffect,
+ isNowAccess: isNowAccess,
+ isBlockTimestampAccess: isBlockTimestampAccess,
+ isBlockBlockHashAccess: isBlockBlockHashAccess,
+ isThisLocalCall: isThisLocalCall,
+ isSuperLocalCall: isSuperLocalCall,
+ isLibraryCall: isLibraryCall,
+ isLocalCallGraphRelevantNode: isLocalCallGraphRelevantNode,
+ isLocalCall: isLocalCall,
+ isWriteOnStateVariable: isWriteOnStateVariable,
+ isStateVariable: isStateVariable,
+ isTransfer: isTransfer,
+ isLowLevelCall: isLowLevelCall,
+ isLowLevelCallInst: isLLCall,
+ isLowLevelCallcodeInst: isLLCallcode,
+ isLowLevelDelegatecallInst: isLLDelegatecall,
+ isLowLevelSendInst: isLLSend,
+ isExternalDirectCall: isExternalDirectCall,
+ isFullyImplementedContract: isFullyImplementedContract,
+ isLibrary: isLibrary,
+ isCallToNonConstLocalFunction: isCallToNonConstLocalFunction,
+ isPlusPlusUnaryOperation: isPlusPlusUnaryOperation,
+ isMinusMinusUnaryOperation: isMinusMinusUnaryOperation,
+ isBuiltinFunctionCall: isBuiltinFunctionCall,
+ isSelfdestructCall: isSelfdestructCall,
+ isAssertCall: isAssertCall,
+ isRequireCall: isRequireCall,
+ isIntDivision: isIntDivision,
+
+ // #################### Trivial Node Identification
+ isDeleteUnaryOperation: isDeleteUnaryOperation,
+ isFunctionDefinition: isFunctionDefinition,
+ isModifierDefinition: isModifierDefinition,
+ isInheritanceSpecifier: isInheritanceSpecifier,
+ isModifierInvocation: isModifierInvocation,
+ isVariableDeclaration: isVariableDeclaration,
+ isStorageVariableDeclaration: isStorageVariableDeclaration,
+ isAssignment: isAssignment,
+ isContractDefinition: isContractDefinition,
+ isConstantFunction: isConstantFunction,
+ isPayableFunction: isPayableFunction,
+ isConstructor: isConstructor,
+ isInlineAssembly: isInlineAssembly,
+ isNewExpression: isNewExpression,
+ isReturn: isReturn,
+
+ // #################### Constants
+ nodeTypes: nodeTypes,
+ basicTypes: basicTypes,
+ basicFunctionTypes: basicFunctionTypes,
+ lowLevelCallTypes: lowLevelCallTypes,
+ specialVariables: specialVariables,
+ helpers: {
+ nrOfChildren: nrOfChildren,
+ minNrOfChildren: minNrOfChildren,
+ expressionType: expressionType,
+ nodeType: nodeType,
+ name: name,
+ operator: operator,
+ buildFunctionSignature: buildFunctionSignature
+ }
+}
diff --git a/remix-analyzer/src/analysis/modules/thisLocal.js b/remix-analyzer/src/analysis/modules/thisLocal.js
new file mode 100644
index 0000000000..2ca7a5975c
--- /dev/null
+++ b/remix-analyzer/src/analysis/modules/thisLocal.js
@@ -0,0 +1,29 @@
+var name = 'This on local calls: '
+var desc = 'Invocation of local functions via this'
+var categories = require('./categories')
+var common = require('./staticAnalysisCommon')
+
+function thisLocal () {
+ this.warningNodes = []
+}
+
+thisLocal.prototype.visit = function (node) {
+ if (common.isThisLocalCall(node)) this.warningNodes.push(node)
+}
+
+thisLocal.prototype.report = function (compilationResults) {
+ return this.warningNodes.map(function (item, i) {
+ return {
+ warning: 'Use of "this" for local functions: Never use this to call functions in the same contract, it only consumes more gas than normal local calls.',
+ location: item.src,
+ more: 'http://solidity.readthedocs.io/en/develop/control-structures.html#external-function-calls'
+ }
+ })
+}
+
+module.exports = {
+ name: name,
+ description: desc,
+ category: categories.GAS,
+ Module: thisLocal
+}
diff --git a/remix-analyzer/src/analysis/modules/txOrigin.js b/remix-analyzer/src/analysis/modules/txOrigin.js
new file mode 100644
index 0000000000..46834e4f8e
--- /dev/null
+++ b/remix-analyzer/src/analysis/modules/txOrigin.js
@@ -0,0 +1,35 @@
+var name = 'Transaction origin: '
+var desc = 'Warn if tx.origin is used'
+var categories = require('./categories')
+
+function txOrigin () {
+ this.txOriginNodes = []
+}
+
+txOrigin.prototype.visit = function (node) {
+ if (node.name === 'MemberAccess' &&
+ node.attributes.member_name === 'origin' &&
+ node.attributes.type === 'address' &&
+ node.children && node.children.length &&
+ node.children[0].attributes.type === 'tx' &&
+ node.children[0].attributes.value === 'tx') {
+ this.txOriginNodes.push(node)
+ }
+}
+
+txOrigin.prototype.report = function () {
+ return this.txOriginNodes.map(function (item, i) {
+ return {
+ warning: `Use of tx.origin: "tx.origin" is useful only in very exceptional cases.
+ If you use it for authentication, you usually want to replace it by "msg.sender", because otherwise any contract you call can act on your behalf.`,
+ location: item.src
+ }
+ })
+}
+
+module.exports = {
+ name: name,
+ description: desc,
+ category: categories.SECURITY,
+ Module: txOrigin
+}
diff --git a/remix-analyzer/src/analysis/staticAnalysisRunner.js b/remix-analyzer/src/analysis/staticAnalysisRunner.js
new file mode 100644
index 0000000000..8f3083d712
--- /dev/null
+++ b/remix-analyzer/src/analysis/staticAnalysisRunner.js
@@ -0,0 +1,57 @@
+'use strict'
+var AstWalker = require('remix-lib').AstWalker
+var list = require('./modules/list')
+
+function staticAnalysisRunner () {
+}
+
+staticAnalysisRunner.prototype.run = function (compilationResult, toRun, callback) {
+ var self = this
+ var modules = toRun.map(function (i) {
+ var m = self.modules()[i]
+ return { 'name': m.name, 'mod': new m.Module() }
+ })
+
+ this.runWithModuleList(compilationResult, modules, callback)
+}
+
+staticAnalysisRunner.prototype.runWithModuleList = function (compilationResult, modules, callback) {
+ var reports = []
+ // Also provide convenience analysis via the AST walker.
+ var walker = new AstWalker()
+ for (var k in compilationResult.sources) {
+ walker.walk(compilationResult.sources[k].legacyAST, {'*': function (node) {
+ modules.map(function (item, i) {
+ if (item.mod.visit !== undefined) {
+ try {
+ item.mod.visit(node)
+ } catch (e) {
+ reports.push({
+ name: item.name, report: [{ warning: 'INTERNAL ERROR in module ' + item.name + ' ' + e.message, error: e.stack }]
+ })
+ }
+ }
+ })
+ return true
+ }})
+ }
+
+ // Here, modules can just collect the results from the AST walk,
+ // but also perform new analysis.
+ reports = reports.concat(modules.map(function (item, i) {
+ var report = null
+ try {
+ report = item.mod.report(compilationResult)
+ } catch (e) {
+ report = [{ warning: 'INTERNAL ERROR in module ' + item.name + ' ' + e.message, error: e.stack }]
+ }
+ return { name: item.name, report: report }
+ }))
+ callback(reports)
+}
+
+staticAnalysisRunner.prototype.modules = function () {
+ return list
+}
+
+module.exports = staticAnalysisRunner
diff --git a/remix-analyzer/test/analysis/staticAnalysisCommon-test.js b/remix-analyzer/test/analysis/staticAnalysisCommon-test.js
new file mode 100644
index 0000000000..7d4fc4a95e
--- /dev/null
+++ b/remix-analyzer/test/analysis/staticAnalysisCommon-test.js
@@ -0,0 +1,2138 @@
+var test = require('tape')
+
+var common = require('../../src/analysis/modules/staticAnalysisCommon')
+
+function escapeRegExp (str) {
+ return str.replace(/[-[\]/{}()+?.\\^$|]/g, '\\$&')
+}
+
+test('staticAnalysisCommon.helpers.buildFunctionSignature', function (t) {
+ t.plan(9)
+
+ t.equal(common.helpers.buildFunctionSignature([common.basicTypes.UINT, common.basicTypes.ADDRESS], [common.basicTypes.BOOL], false),
+ 'function (uint256,address) returns (bool)',
+ 'two params and return value without payable')
+
+ t.equal(common.helpers.buildFunctionSignature([common.basicTypes.UINT, common.basicTypes.ADDRESS], [common.basicTypes.BOOL], false, 'pure'),
+ 'function (uint256,address) pure returns (bool)',
+ 'two params and return value without payable but pure')
+
+ t.equal(common.helpers.buildFunctionSignature([common.basicTypes.UINT, common.basicTypes.ADDRESS], [common.basicTypes.BOOL], true, 'pure'),
+ 'function (uint256,address) payable pure returns (bool)',
+ 'two params and return value without payable but pure')
+
+ t.equal(common.helpers.buildFunctionSignature([common.basicTypes.UINT, common.basicTypes.BYTES32, common.basicTypes.BYTES32], [], true),
+ 'function (uint256,bytes32,bytes32) payable',
+ 'three params and no return with payable')
+
+ t.equal(common.helpers.buildFunctionSignature([common.basicTypes.BOOL], [common.basicTypes.BYTES32, common.basicTypes.ADDRESS], true),
+ 'function (bool) payable returns (bytes32,address)',
+ 'one param and two return values with payable')
+
+ t.equal(common.lowLevelCallTypes.CALL.type,
+ 'function () payable returns (bool)',
+ 'check fixed call type')
+
+ t.equal(common.lowLevelCallTypes.CALLCODE.type,
+ 'function () payable returns (bool)',
+ 'check fixed callcode type')
+
+ t.equal(common.lowLevelCallTypes.SEND.type,
+ 'function (uint256) returns (bool)',
+ 'check fixed send type')
+
+ t.equal(common.lowLevelCallTypes.DELEGATECALL.type,
+ 'function () returns (bool)',
+ 'check fixed call type')
+})
+
+// #################### Node Identification Primitives
+
+test('staticAnalysisCommon.helpers.name', function (t) {
+ t.plan(9)
+ var node = { attributes: { value: 'now' } }
+ var node2 = { attributes: { member_name: 'call' } }
+
+ t.ok(common.helpers.name(node, 'now'), 'should work for values')
+ t.ok(common.helpers.name(node2, 'call'), 'should work for member_name')
+ t.ok(common.helpers.name(node2, '.all'), 'regex should work')
+
+ lowlevelAccessersCommon(t, common.helpers.name, node)
+})
+
+test('staticAnalysisCommon.helpers.operator', function (t) {
+ t.plan(10)
+ var node = { attributes: { operator: '++' } }
+ var node2 = { attributes: { operator: '+++' } }
+
+ var escapedPP = escapeRegExp('++')
+ var escapedPPExact = `^${escapedPP}$`
+
+ t.ok(common.helpers.operator(node, escapedPPExact), 'should work for ++')
+ t.notOk(common.helpers.operator(node2, escapedPPExact), 'should not work for +++')
+ t.ok(common.helpers.operator(node, escapedPP), 'should work for ++')
+ t.ok(common.helpers.operator(node2, escapedPP), 'should work for +++')
+
+ lowlevelAccessersCommon(t, common.helpers.operator, node)
+})
+
+test('staticAnalysisCommon.helpers.nodeType', function (t) {
+ t.plan(9)
+ var node = { name: 'Identifier', attributes: { name: 'now' } }
+ var node2 = { name: 'FunctionCall', attributes: { member_name: 'call' } }
+
+ t.ok(common.helpers.nodeType(node, common.nodeTypes.IDENTIFIER), 'should work for ident')
+ t.ok(common.helpers.nodeType(node2, common.nodeTypes.FUNCTIONCALL), 'should work for funcall')
+ t.ok(common.helpers.nodeType(node2, '^F'), 'regex should work for funcall')
+
+ lowlevelAccessersCommon(t, common.helpers.nodeType, node)
+})
+
+test('staticAnalysisCommon.helpers.expressionType', function (t) {
+ t.plan(9)
+ var node = { name: 'Identifier', attributes: { value: 'now', type: 'uint256' } }
+ var node2 = { name: 'FunctionCall', attributes: { member_name: 'call', type: 'function () payable returns (bool)' } }
+
+ t.ok(common.helpers.expressionType(node, common.basicTypes.UINT), 'should work for ident')
+ t.ok(common.helpers.expressionType(node2, escapeRegExp(common.basicFunctionTypes.CALL)), 'should work for funcall')
+ t.ok(common.helpers.expressionType(node2, '^function \\('), 'regex should work')
+
+ lowlevelAccessersCommon(t, common.helpers.expressionType, node)
+})
+
+test('staticAnalysisCommon.helpers.nrOfChildren', function (t) {
+ t.plan(10)
+ var node = { name: 'Identifier', children: ['a', 'b'], attributes: { value: 'now', type: 'uint256' } }
+ var node2 = { name: 'FunctionCall', children: [], attributes: { member_name: 'call', type: 'function () payable returns (bool)' } }
+ var node3 = { name: 'FunctionCall', attributes: { member_name: 'call', type: 'function () payable returns (bool)' } }
+
+ t.ok(common.helpers.nrOfChildren(node, 2), 'should work for 2 children')
+ t.notOk(common.helpers.nrOfChildren(node, '1+2'), 'regex should not work')
+ t.ok(common.helpers.nrOfChildren(node2, 0), 'should work for 0 children')
+ t.ok(common.helpers.nrOfChildren(node3, 0), 'should work without children arr')
+
+ lowlevelAccessersCommon(t, common.helpers.nrOfChildren, node)
+})
+
+test('staticAnalysisCommon.helpers.minNrOfChildren', function (t) {
+ t.plan(13)
+ var node = { name: 'Identifier', children: ['a', 'b'], attributes: { value: 'now', type: 'uint256' } }
+ var node2 = { name: 'FunctionCall', children: [], attributes: { member_name: 'call', type: 'function () payable returns (bool)' } }
+ var node3 = { name: 'FunctionCall', attributes: { member_name: 'call', type: 'function () payable returns (bool)' } }
+
+ t.ok(common.helpers.minNrOfChildren(node, 2), 'should work for 2 children')
+ t.ok(common.helpers.minNrOfChildren(node, 1), 'should work for 1 children')
+ t.ok(common.helpers.minNrOfChildren(node, 0), 'should work for 0 children')
+ t.notOk(common.helpers.minNrOfChildren(node, 3), 'has less than 3 children')
+ t.notOk(common.helpers.minNrOfChildren(node, '1+2'), 'regex should not work')
+ t.ok(common.helpers.minNrOfChildren(node2, 0), 'should work for 0 children')
+ t.ok(common.helpers.minNrOfChildren(node3, 0), 'should work without children arr')
+
+ lowlevelAccessersCommon(t, common.helpers.minNrOfChildren, node)
+})
+
+function lowlevelAccessersCommon (t, f, someNode) {
+ t.ok(f(someNode), 'always ok if type is undefinded')
+ t.ok(f(someNode, undefined), 'always ok if name is undefinded 2')
+ t.notOk(f(null, undefined), 'false on no node')
+ t.notOk(f(null, 'call'), 'false on no node')
+ t.notOk(f(undefined, null), 'false on no node')
+ t.notOk(f(), 'false on no params')
+}
+
+// #################### Trivial Getter Test
+
+test('staticAnalysisCommon.getType', function (t) {
+ t.plan(3)
+ var node = { name: 'Identifier', children: ['a', 'b'], attributes: { value: 'now', type: 'uint256' } }
+ var node2 = { name: 'FunctionCall', children: [], attributes: { member_name: 'call', type: 'function () payable returns (bool)' } }
+ var node3 = { name: 'FunctionCall', attributes: { member_name: 'call', type: 'function () payable returns (bool)' } }
+
+ t.ok(common.getType(node) === 'uint256', 'gettype should work for different nodes')
+ t.ok(common.getType(node2) === 'function () payable returns (bool)', 'gettype should work for different nodes')
+ t.ok(common.getType(node3) === 'function () payable returns (bool)', 'gettype should work for different nodes')
+})
+
+// #################### Complex Getter Test
+
+test('staticAnalysisCommon.getFunctionCallType', function (t) {
+ t.plan(5)
+ var localCall = {
+ 'attributes': {
+ 'type': 'tuple()',
+ 'type_conversion': false
+ },
+ 'children': [
+ {
+ 'attributes': {
+ 'type': 'function (struct Ballot.Voter storage pointer)',
+ 'value': 'bli'
+ },
+ 'id': 37,
+ 'name': 'Identifier',
+ 'src': '540:3:0'
+ },
+ {
+ 'attributes': {
+ 'type': 'struct Ballot.Voter storage pointer',
+ 'value': 'x'
+ },
+ 'id': 38,
+ 'name': 'Identifier',
+ 'src': '544:1:0'
+ }
+ ],
+ 'id': 39,
+ 'name': 'FunctionCall',
+ 'src': '540:6:0'
+ }
+ var thisLocalCall = { name: 'MemberAccess', children: [ { attributes: { value: 'this', type: 'contract test' }, name: 'Identifier' } ], attributes: { value: 'b', type: 'function (bytes32,address) returns (bool)' } }
+ var externalDirect = {
+ attributes: {
+ member_name: 'info',
+ type: 'function () payable external returns (uint256)'
+ },
+ children: [
+ {
+ attributes: {
+ type: 'contract InfoFeed',
+ value: 'f'
+ },
+ id: 30,
+ name: 'Identifier',
+ src: '405:1:0'
+ }
+ ],
+ id: 32,
+ name: 'MemberAccess',
+ src: '405:6:0'
+ }
+ var libCall = {
+ 'attributes': {
+ 'member_name': 'insert',
+ 'type': 'function (struct Set.Data storage pointer,uint256) returns (bool)'
+ },
+ 'children': [
+ {
+ 'attributes': {
+ 'type': 'type(library Set)',
+ 'value': 'Set'
+ },
+ 'name': 'Identifier'
+ }
+ ],
+ 'name': 'MemberAccess'
+ }
+ t.equal(common.getFunctionCallType(libCall), 'function (struct Set.Data storage pointer,uint256) returns (bool)', 'this lib call returns correct type')
+ t.equal(common.getFunctionCallType(thisLocalCall), 'function (bytes32,address) returns (bool)', 'this local call returns correct type')
+ t.equal(common.getFunctionCallType(externalDirect), 'function () payable external returns (uint256)', 'external direct call returns correct type')
+ t.equal(common.getFunctionCallType(localCall), 'function (struct Ballot.Voter storage pointer)', 'local call returns correct type')
+ t.throws(() => common.getFunctionCallType({ name: 'MemberAccess' }), undefined, 'throws on wrong type')
+})
+
+test('staticAnalysisCommon.getEffectedVariableName', function (t) {
+ t.plan(3)
+ var assignment = {
+ 'attributes': {
+ 'operator': '=',
+ 'type': 'uint256'
+ },
+ 'children': [
+ {
+ 'attributes': {
+ 'type': 'uint256'
+ },
+ 'children': [
+ {
+ 'attributes': {
+ 'type': 'mapping(address => uint256)',
+ 'value': 'c'
+ },
+ 'id': 61,
+ 'name': 'Identifier',
+ 'src': '873:1:0'
+ },
+ {
+ 'attributes': {
+ 'member_name': 'sender',
+ 'type': 'address'
+ },
+ 'children': [
+ {
+ 'attributes': {
+ 'type': 'msg',
+ 'value': 'msg'
+ },
+ 'id': 62,
+ 'name': 'Identifier',
+ 'src': '875:3:0'
+ }
+ ],
+ 'id': 63,
+ 'name': 'MemberAccess',
+ 'src': '875:10:0'
+ }
+ ],
+ 'id': 64,
+ 'name': 'IndexAccess',
+ 'src': '873:13:0'
+ },
+ {
+ 'attributes': {
+ 'hexvalue': '30',
+ 'subdenomination': null,
+ 'token': null,
+ 'type': 'int_const 0',
+ 'value': '0'
+ },
+ 'id': 65,
+ 'name': 'Literal',
+ 'src': '889:1:0'
+ }
+ ],
+ 'id': 66,
+ 'name': 'Assignment',
+ 'src': '873:17:0'
+ }
+ var inlineAssembly = {
+ 'children': [
+ ],
+ 'id': 21,
+ 'name': 'InlineAssembly',
+ 'src': '809:41:0'
+ }
+ t.throws(() => common.getEffectedVariableName(inlineAssembly), 'staticAnalysisCommon.js: not an effect Node or inline assembly', 'get from inline assembly should throw')
+ t.ok(common.getEffectedVariableName(assignment) === 'c', 'get right name for assignment')
+ t.throws(() => common.getEffectedVariableName({ name: 'MemberAccess' }), undefined, 'should throw on all other nodes')
+})
+
+test('staticAnalysisCommon.getLocalCallName', function (t) {
+ t.plan(3)
+ var localCall = {
+ 'attributes': {
+ 'type': 'tuple()',
+ 'type_conversion': false
+ },
+ 'children': [
+ {
+ 'attributes': {
+ 'type': 'function (struct Ballot.Voter storage pointer)',
+ 'value': 'bli'
+ },
+ 'id': 37,
+ 'name': 'Identifier',
+ 'src': '540:3:0'
+ },
+ {
+ 'attributes': {
+ 'type': 'struct Ballot.Voter storage pointer',
+ 'value': 'x'
+ },
+ 'id': 38,
+ 'name': 'Identifier',
+ 'src': '544:1:0'
+ }
+ ],
+ 'id': 39,
+ 'name': 'FunctionCall',
+ 'src': '540:6:0'
+ }
+ var thisLocalCall = { name: 'MemberAccess', children: [ { attributes: { value: 'this', type: 'contract test' }, name: 'Identifier' } ], attributes: { value: 'b', type: 'function (bytes32,address) returns (bool)' } }
+ var externalDirect = {
+ attributes: {
+ member_name: 'info',
+ type: 'function () payable external returns (uint256)'
+ },
+ children: [
+ {
+ attributes: {
+ type: 'contract InfoFeed',
+ value: 'f'
+ },
+ id: 30,
+ name: 'Identifier',
+ src: '405:1:0'
+ }
+ ],
+ id: 32,
+ name: 'MemberAccess',
+ src: '405:6:0'
+ }
+ t.ok(common.getLocalCallName(localCall) === 'bli', 'getLocal call name from node')
+ t.throws(() => common.getLocalCallName(externalDirect), undefined, 'throws on other nodes')
+ t.throws(() => common.getLocalCallName(thisLocalCall), undefined, 'throws on other nodes')
+})
+
+test('staticAnalysisCommon.getThisLocalCallName', function (t) {
+ t.plan(3)
+ var localCall = {
+ 'attributes': {
+ 'type': 'tuple()',
+ 'type_conversion': false
+ },
+ 'children': [
+ {
+ 'attributes': {
+ 'type': 'function (struct Ballot.Voter storage pointer)',
+ 'value': 'bli'
+ },
+ 'id': 37,
+ 'name': 'Identifier',
+ 'src': '540:3:0'
+ },
+ {
+ 'attributes': {
+ 'type': 'struct Ballot.Voter storage pointer',
+ 'value': 'x'
+ },
+ 'id': 38,
+ 'name': 'Identifier',
+ 'src': '544:1:0'
+ }
+ ],
+ 'id': 39,
+ 'name': 'FunctionCall',
+ 'src': '540:6:0'
+ }
+ var thisLocalCall = { name: 'MemberAccess', children: [ { attributes: { value: 'this', type: 'contract test' }, name: 'Identifier' } ], attributes: { value: 'b', type: 'function (bytes32,address) returns (bool)' } }
+ var externalDirect = {
+ attributes: {
+ member_name: 'info',
+ type: 'function () payable external returns (uint256)'
+ },
+ children: [
+ {
+ attributes: {
+ type: 'contract InfoFeed',
+ value: 'f'
+ },
+ id: 30,
+ name: 'Identifier',
+ src: '405:1:0'
+ }
+ ],
+ id: 32,
+ name: 'MemberAccess',
+ src: '405:6:0'
+ }
+ t.ok(common.getThisLocalCallName(thisLocalCall) === 'b', 'get this Local call name from node')
+ t.throws(() => common.getThisLocalCallName(externalDirect), undefined, 'throws on other nodes')
+ t.throws(() => common.getThisLocalCallName(localCall), undefined, 'throws on other nodes')
+})
+
+test('staticAnalysisCommon.getSuperLocalCallName', function (t) {
+ t.plan(4)
+ var superLocal = {
+ 'attributes': {
+ 'member_name': 'duper',
+ 'type': 'function ()'
+ },
+ 'children': [
+ {
+ 'attributes': {
+ 'type': 'contract super a',
+ 'value': 'super'
+ },
+ 'name': 'Identifier'
+ }
+ ],
+ 'name': 'MemberAccess'
+ }
+ var localCall = {
+ 'attributes': {
+ 'type': 'tuple()',
+ 'type_conversion': false
+ },
+ 'children': [
+ {
+ 'attributes': {
+ 'type': 'function (struct Ballot.Voter storage pointer)',
+ 'value': 'bli'
+ },
+ 'id': 37,
+ 'name': 'Identifier',
+ 'src': '540:3:0'
+ },
+ {
+ 'attributes': {
+ 'type': 'struct Ballot.Voter storage pointer',
+ 'value': 'x'
+ },
+ 'id': 38,
+ 'name': 'Identifier',
+ 'src': '544:1:0'
+ }
+ ],
+ 'id': 39,
+ 'name': 'FunctionCall',
+ 'src': '540:6:0'
+ }
+ var thisLocalCall = { name: 'MemberAccess', children: [ { attributes: { value: 'this', type: 'contract test' }, name: 'Identifier' } ], attributes: { value: 'b', type: 'function (bytes32,address) returns (bool)' } }
+ var externalDirect = {
+ attributes: {
+ member_name: 'info',
+ type: 'function () payable external returns (uint256)'
+ },
+ children: [
+ {
+ attributes: {
+ type: 'contract InfoFeed',
+ value: 'f'
+ },
+ id: 30,
+ name: 'Identifier',
+ src: '405:1:0'
+ }
+ ],
+ id: 32,
+ name: 'MemberAccess',
+ src: '405:6:0'
+ }
+ t.equal(common.getSuperLocalCallName(superLocal), 'duper', 'get local name from super local call')
+ t.throws(() => common.getSuperLocalCallName(thisLocalCall), 'throws on other nodes')
+ t.throws(() => common.getSuperLocalCallName(externalDirect), undefined, 'throws on other nodes')
+ t.throws(() => common.getSuperLocalCallName(localCall), undefined, 'throws on other nodes')
+})
+
+test('staticAnalysisCommon.getExternalDirectCallContractName', function (t) {
+ t.plan(3)
+ var localCall = {
+ 'attributes': {
+ 'type': 'tuple()',
+ 'type_conversion': false
+ },
+ 'children': [
+ {
+ 'attributes': {
+ 'type': 'function (struct Ballot.Voter storage pointer)',
+ 'value': 'bli'
+ },
+ 'id': 37,
+ 'name': 'Identifier',
+ 'src': '540:3:0'
+ },
+ {
+ 'attributes': {
+ 'type': 'struct Ballot.Voter storage pointer',
+ 'value': 'x'
+ },
+ 'id': 38,
+ 'name': 'Identifier',
+ 'src': '544:1:0'
+ }
+ ],
+ 'id': 39,
+ 'name': 'FunctionCall',
+ 'src': '540:6:0'
+ }
+ var thisLocalCall = { name: 'MemberAccess', children: [ { attributes: { value: 'this', type: 'contract test' }, name: 'Identifier' } ], attributes: { value: 'b', type: 'function (bytes32,address) returns (bool)' } }
+ var externalDirect = {
+ attributes: {
+ member_name: 'info',
+ type: 'function () payable external returns (uint256)'
+ },
+ children: [
+ {
+ attributes: {
+ type: 'contract InfoFeed',
+ value: 'f'
+ },
+ id: 30,
+ name: 'Identifier',
+ src: '405:1:0'
+ }
+ ],
+ id: 32,
+ name: 'MemberAccess',
+ src: '405:6:0'
+ }
+
+ t.ok(common.getExternalDirectCallContractName(externalDirect) === 'InfoFeed', 'external direct call contract name from node')
+ t.throws(() => common.getExternalDirectCallContractName(thisLocalCall), undefined, 'throws on other nodes')
+ t.throws(() => common.getExternalDirectCallContractName(localCall), undefined, 'throws on other nodes')
+})
+
+test('staticAnalysisCommon.getThisLocalCallContractName', function (t) {
+ t.plan(3)
+ var localCall = {
+ 'attributes': {
+ 'type': 'tuple()',
+ 'type_conversion': false
+ },
+ 'children': [
+ {
+ 'attributes': {
+ 'type': 'function (struct Ballot.Voter storage pointer)',
+ 'value': 'bli'
+ },
+ 'id': 37,
+ 'name': 'Identifier',
+ 'src': '540:3:0'
+ },
+ {
+ 'attributes': {
+ 'type': 'struct Ballot.Voter storage pointer',
+ 'value': 'x'
+ },
+ 'id': 38,
+ 'name': 'Identifier',
+ 'src': '544:1:0'
+ }
+ ],
+ 'id': 39,
+ 'name': 'FunctionCall',
+ 'src': '540:6:0'
+ }
+ var thisLocalCall = { name: 'MemberAccess', children: [ { attributes: { value: 'this', type: 'contract test' }, name: 'Identifier' } ], attributes: { value: 'b', type: 'function (bytes32,address) returns (bool)' } }
+ var externalDirect = {
+ attributes: {
+ member_name: 'info',
+ type: 'function () payable external returns (uint256)'
+ },
+ children: [
+ {
+ attributes: {
+ type: 'contract InfoFeed',
+ value: 'f'
+ },
+ id: 30,
+ name: 'Identifier',
+ src: '405:1:0'
+ }
+ ],
+ id: 32,
+ name: 'MemberAccess',
+ src: '405:6:0'
+ }
+ t.ok(common.getThisLocalCallContractName(thisLocalCall) === 'test', 'this local call contract name from node')
+ t.throws(() => common.getThisLocalCallContractName(localCall), undefined, 'throws on other nodes')
+ t.throws(() => common.getThisLocalCallContractName(externalDirect), undefined, 'throws on other nodes')
+})
+
+test('staticAnalysisCommon.getExternalDirectCallMemberName', function (t) {
+ t.plan(3)
+ var localCall = {
+ 'attributes': {
+ 'type': 'tuple()',
+ 'type_conversion': false
+ },
+ 'children': [
+ {
+ 'attributes': {
+ 'type': 'function (struct Ballot.Voter storage pointer)',
+ 'value': 'bli'
+ },
+ 'id': 37,
+ 'name': 'Identifier',
+ 'src': '540:3:0'
+ },
+ {
+ 'attributes': {
+ 'type': 'struct Ballot.Voter storage pointer',
+ 'value': 'x'
+ },
+ 'id': 38,
+ 'name': 'Identifier',
+ 'src': '544:1:0'
+ }
+ ],
+ 'id': 39,
+ 'name': 'FunctionCall',
+ 'src': '540:6:0'
+ }
+ var thisLocalCall = { name: 'MemberAccess', children: [ { attributes: { value: 'this', type: 'contract test' }, name: 'Identifier' } ], attributes: { value: 'b', type: 'function (bytes32,address) returns (bool)' } }
+ var externalDirect = {
+ attributes: {
+ member_name: 'info',
+ type: 'function () payable external returns (uint256)'
+ },
+ children: [
+ {
+ attributes: {
+ type: 'contract InfoFeed',
+ value: 'f'
+ },
+ id: 30,
+ name: 'Identifier',
+ src: '405:1:0'
+ }
+ ],
+ id: 32,
+ name: 'MemberAccess',
+ src: '405:6:0'
+ }
+ t.ok(common.getExternalDirectCallMemberName(externalDirect) === 'info', 'external direct call name from node')
+ t.throws(() => common.getExternalDirectCallMemberName(thisLocalCall), undefined, 'throws on other nodes')
+ t.throws(() => common.getExternalDirectCallMemberName(localCall), undefined, 'throws on other nodes')
+})
+
+test('staticAnalysisCommon.getContractName', function (t) {
+ t.plan(2)
+ var contract = { name: 'ContractDefinition', attributes: { name: 'baz' } }
+ t.ok(common.getContractName(contract) === 'baz', 'returns right contract name')
+ t.throws(() => common.getContractName({ name: 'InheritanceSpecifier' }), undefined, 'throws on other nodes')
+})
+
+test('staticAnalysisCommon.getFunctionDefinitionName', function (t) {
+ t.plan(2)
+ var func = { name: 'FunctionDefinition', attributes: { name: 'foo' } }
+ t.ok(common.getFunctionDefinitionName(func) === 'foo', 'returns right contract name')
+ t.throws(() => common.getFunctionDefinitionName({ name: 'InlineAssembly' }), undefined, 'throws on other nodes')
+})
+
+test('staticAnalysisCommon.getInheritsFromName', function (t) {
+ t.plan(2)
+ var inh = {
+ 'children': [
+ {
+ 'attributes': {
+ 'name': 'r'
+ },
+ 'id': 7,
+ 'name': 'UserDefinedTypeName',
+ 'src': '84:1:0'
+ }
+ ],
+ 'id': 8,
+ 'name': 'InheritanceSpecifier',
+ 'src': '84:1:0'
+ }
+ t.ok(common.getInheritsFromName(inh) === 'r', 'returns right contract name')
+ t.throws(() => common.getInheritsFromName({ name: 'ElementaryTypeName' }), undefined, 'throws on other nodes')
+})
+
+test('staticAnalysisCommon.getDeclaredVariableName', function (t) {
+ t.plan(2)
+ var node1 = {
+ 'attributes': {
+ 'name': 'x',
+ 'type': 'struct Ballot.Voter storage pointer'
+ },
+ 'children': [
+ {
+ 'attributes': {
+ 'name': 'Voter'
+ },
+ 'id': 43,
+ 'name': 'UserDefinedTypeName',
+ 'src': '604:5:0'
+ }
+ ],
+ 'id': 44,
+ 'name': 'VariableDeclaration',
+ 'src': '604:15:0'
+ }
+ t.ok(common.getDeclaredVariableName(node1) === 'x', 'extract right variable name')
+ node1.name = 'FunctionCall'
+ t.throws(() => common.getDeclaredVariableName(node1) === 'x', undefined, 'throw if wrong node')
+})
+
+test('staticAnalysisCommon.getStateVariableDeclarationsFormContractNode', function (t) {
+ t.plan(4)
+ var contract = {
+ 'attributes': {
+ 'fullyImplemented': true,
+ 'isLibrary': false,
+ 'linearizedBaseContracts': [
+ 274
+ ],
+ 'name': 'Ballot'
+ },
+ 'children': [
+ {
+ 'attributes': {
+ 'name': 'Voter'
+ },
+ 'children': [],
+ 'name': 'StructDefinition'
+ },
+ {
+ 'attributes': {
+ 'name': 'Proposal'
+ },
+ 'children': [],
+ 'name': 'StructDefinition'
+ },
+ {
+ 'attributes': {
+ 'name': 'chairperson',
+ 'type': 'address'
+ },
+ 'children': [
+ {
+ 'attributes': {
+ 'name': 'address'
+ },
+ 'name': 'ElementaryTypeName'
+ }
+ ],
+ 'name': 'VariableDeclaration'
+ },
+ {
+ 'attributes': {
+ 'name': 'voters',
+ 'type': 'mapping(address => struct Ballot.Voter storage ref)'
+ },
+ 'children': [
+ {
+ 'children': [
+ {
+ 'attributes': {
+ 'name': 'address'
+ },
+ 'name': 'ElementaryTypeName'
+ },
+ {
+ 'attributes': {
+ 'name': 'Voter'
+ },
+ 'name': 'UserDefinedTypeName'
+ }
+ ],
+ 'name': 'Mapping'
+ }
+ ],
+ 'name': 'VariableDeclaration'
+ },
+ {
+ 'attributes': {
+ 'name': 'proposals',
+ 'type': 'struct Ballot.Proposal storage ref[] storage ref'
+ },
+ 'children': [
+ {
+ 'children': [
+ {
+ 'attributes': {
+ 'name': 'Proposal'
+ },
+ 'name': 'UserDefinedTypeName'
+ }
+ ],
+ 'name': 'ArrayTypeName'
+ }
+ ],
+ 'name': 'VariableDeclaration'
+ },
+ {
+ 'attributes': {
+ 'constant': false,
+ 'name': 'Ballot',
+ 'payable': false,
+ 'visibility': 'public'
+ },
+ 'children': [],
+ 'name': 'FunctionDefinition'
+ },
+ {
+ 'attributes': {
+ 'constant': false,
+ 'name': 'giveRightToVote',
+ 'payable': false,
+ 'visibility': 'public'
+ },
+ 'children': [],
+ 'name': 'FunctionDefinition'
+ }
+ ],
+ 'name': 'ContractDefinition'
+ }
+ var res = common.getStateVariableDeclarationsFormContractNode(contract).map(common.getDeclaredVariableName)
+
+ t.ok(res[0] === 'chairperson', 'var 1 should be ')
+ t.ok(res[1] === 'voters', 'var 2 should be ')
+ t.ok(res[2] === 'proposals', 'var 3 should be ')
+ t.ok(res[3] === undefined, 'var 4 should be undefined')
+})
+
+test('staticAnalysisCommon.getFunctionOrModifierDefinitionParameterPart', function (t) {
+ t.plan(2)
+ var funDef = {
+ 'attributes': {
+ 'constant': true,
+ 'name': 'winnerName',
+ 'payable': false,
+ 'visibility': 'public'
+ },
+ 'children': [
+ {
+ 'children': [
+ ],
+ 'name': 'ParameterList'
+ },
+ {
+ 'children': [],
+ 'name': 'ParameterList'
+ },
+ {
+ 'children': [],
+ 'name': 'Block'
+ }
+ ],
+ 'name': 'FunctionDefinition'
+ }
+ t.ok(common.helpers.nodeType(common.getFunctionOrModifierDefinitionParameterPart(funDef), 'ParameterList'), 'should return a parameterList')
+ t.throws(() => common.getFunctionOrModifierDefinitionParameterPart({ name: 'SourceUnit' }), undefined, 'throws on other nodes')
+})
+
+test('staticAnalysisCommon.getFunctionCallTypeParameterType', function (t) {
+ t.plan(4)
+ var localCall = {
+ 'attributes': {
+ 'type': 'tuple()',
+ 'type_conversion': false
+ },
+ 'children': [
+ {
+ 'attributes': {
+ 'type': 'function (struct Ballot.Voter storage pointer)',
+ 'value': 'bli'
+ },
+ 'id': 37,
+ 'name': 'Identifier',
+ 'src': '540:3:0'
+ },
+ {
+ 'attributes': {
+ 'type': 'struct Ballot.Voter storage pointer',
+ 'value': 'x'
+ },
+ 'id': 38,
+ 'name': 'Identifier',
+ 'src': '544:1:0'
+ }
+ ],
+ 'id': 39,
+ 'name': 'FunctionCall',
+ 'src': '540:6:0'
+ }
+ var thisLocalCall = { name: 'MemberAccess', children: [ { attributes: { value: 'this', type: 'contract test' }, name: 'Identifier' } ], attributes: { value: 'b', type: 'function (bytes32,address) returns (bool)' } }
+ var externalDirect = {
+ attributes: {
+ member_name: 'info',
+ type: 'function () payable external returns (uint256)'
+ },
+ children: [
+ {
+ attributes: {
+ type: 'contract InfoFeed',
+ value: 'f'
+ },
+ id: 30,
+ name: 'Identifier',
+ src: '405:1:0'
+ }
+ ],
+ id: 32,
+ name: 'MemberAccess',
+ src: '405:6:0'
+ }
+ t.ok(common.getFunctionCallTypeParameterType(thisLocalCall) === 'bytes32,address', 'this local call returns correct type')
+ t.ok(common.getFunctionCallTypeParameterType(externalDirect) === '', 'external direct call returns correct type')
+ t.ok(common.getFunctionCallTypeParameterType(localCall) === 'struct Ballot.Voter storage pointer', 'local call returns correct type')
+ t.throws(() => common.getFunctionCallTypeParameterType({ name: 'MemberAccess' }), undefined, 'throws on wrong type')
+})
+
+test('staticAnalysisCommon.getLibraryCallContractName', function (t) {
+ t.plan(2)
+ var node = {
+ 'attributes': {
+ 'member_name': 'insert',
+ 'type': 'function (struct Set.Data storage pointer,uint256) returns (bool)'
+ },
+ 'children': [
+ {
+ 'attributes': {
+ 'type': 'type(library Set)',
+ 'value': 'Set'
+ },
+ 'name': 'Identifier'
+ }
+ ],
+ 'name': 'MemberAccess'
+ }
+ t.equal(common.getLibraryCallContractName(node), 'Set', 'should return correct contract name')
+ t.throws(() => common.getLibraryCallContractName({ name: 'Identifier' }), undefined, 'should throw on wrong node')
+})
+
+test('staticAnalysisCommon.getLibraryCallMemberName', function (t) {
+ t.plan(2)
+ var node = {
+ 'attributes': {
+ 'member_name': 'insert',
+ 'type': 'function (struct Set.Data storage pointer,uint256) returns (bool)'
+ },
+ 'children': [
+ {
+ 'attributes': {
+ 'type': 'type(library Set)',
+ 'value': 'Set'
+ },
+ 'name': 'Identifier'
+ }
+ ],
+ 'name': 'MemberAccess'
+ }
+ t.equal(common.getLibraryCallMemberName(node), 'insert', 'should return correct member name')
+ t.throws(() => common.getLibraryCallMemberName({ name: 'Identifier' }), undefined, 'should throw on wrong node')
+})
+
+test('staticAnalysisCommon.getFullQualifiedFunctionCallIdent', function (t) {
+ t.plan(4)
+ var contract = { name: 'ContractDefinition', attributes: { name: 'baz' } }
+ var localCall = {
+ 'attributes': {
+ 'type': 'tuple()',
+ 'type_conversion': false
+ },
+ 'children': [
+ {
+ 'attributes': {
+ 'type': 'function (struct Ballot.Voter storage pointer)',
+ 'value': 'bli'
+ },
+ 'id': 37,
+ 'name': 'Identifier',
+ 'src': '540:3:0'
+ },
+ {
+ 'attributes': {
+ 'type': 'struct Ballot.Voter storage pointer',
+ 'value': 'x'
+ },
+ 'id': 38,
+ 'name': 'Identifier',
+ 'src': '544:1:0'
+ }
+ ],
+ 'id': 39,
+ 'name': 'FunctionCall',
+ 'src': '540:6:0'
+ }
+ var thisLocalCall = { name: 'MemberAccess', children: [ { attributes: { value: 'this', type: 'contract test' }, name: 'Identifier' } ], attributes: { value: 'b', type: 'function (bytes32,address) returns (bool)' } }
+ var externalDirect = {
+ attributes: {
+ member_name: 'info',
+ type: 'function () payable external returns (uint256)'
+ },
+ children: [
+ {
+ attributes: {
+ type: 'contract InfoFeed',
+ value: 'f'
+ },
+ id: 30,
+ name: 'Identifier',
+ src: '405:1:0'
+ }
+ ],
+ id: 32,
+ name: 'MemberAccess',
+ src: '405:6:0'
+ }
+
+ t.ok(common.getFullQualifiedFunctionCallIdent(contract, thisLocalCall) === 'test.b(bytes32,address)', 'this local call returns correct type')
+ t.ok(common.getFullQualifiedFunctionCallIdent(contract, externalDirect) === 'InfoFeed.info()', 'external direct call returns correct type')
+ t.ok(common.getFullQualifiedFunctionCallIdent(contract, localCall) === 'baz.bli(struct Ballot.Voter storage pointer)', 'local call returns correct type')
+ t.throws(() => common.getFullQualifiedFunctionCallIdent(contract, { name: 'MemberAccess' }), undefined, 'throws on wrong type')
+})
+
+test('staticAnalysisCommon.getFullQuallyfiedFuncDefinitionIdent', function (t) {
+ t.plan(3)
+ var contract = { name: 'ContractDefinition', attributes: { name: 'baz' } }
+ var funDef = {
+ 'attributes': {
+ 'constant': false,
+ 'name': 'getY',
+ 'payable': false,
+ 'visibility': 'public'
+ },
+ 'children': [
+ {
+ 'children': [
+ {
+ 'attributes': {
+ 'name': 'z',
+ 'type': 'uint256'
+ },
+ 'children': [
+ {
+ 'attributes': {
+ 'name': 'uint'
+ },
+ 'name': 'ElementaryTypeName'
+ }
+ ],
+ 'name': 'VariableDeclaration'
+ },
+ {
+ 'attributes': {
+ 'name': 'r',
+ 'type': 'bool'
+ },
+ 'children': [
+ {
+ 'attributes': {
+ 'name': 'bool'
+ },
+ 'name': 'ElementaryTypeName'
+ }
+ ],
+ 'name': 'VariableDeclaration'
+ }
+ ],
+ 'name': 'ParameterList'
+ },
+ {
+ 'children': [
+ {
+ 'attributes': {
+ 'name': '',
+ 'type': 'uint256'
+ },
+ 'children': [
+ {
+ 'attributes': {
+ 'name': 'uint'
+ },
+ 'id': 34,
+ 'name': 'ElementaryTypeName',
+ 'src': '285:4:0'
+ }
+ ],
+ 'id': 35,
+ 'name': 'VariableDeclaration',
+ 'src': '285:4:0'
+ }
+ ],
+ 'name': 'ParameterList'
+ },
+ {
+ 'children': [],
+ 'name': 'Block'
+ }
+ ],
+ 'name': 'FunctionDefinition'
+ }
+ t.ok(common.getFullQuallyfiedFuncDefinitionIdent(contract, funDef, ['uint256', 'bool']) === 'baz.getY(uint256,bool)', 'creates right signature')
+ t.throws(() => common.getFullQuallyfiedFuncDefinitionIdent(contract, { name: 'MemberAccess' }, ['uint256', 'bool']), undefined, 'throws on wrong nodes')
+ t.throws(() => common.getFullQuallyfiedFuncDefinitionIdent({ name: 'FunctionCall' }, funDef, ['uint256', 'bool']), undefined, 'throws on wrong nodes')
+})
+
+// #################### Trivial Node Identification
+
+test('staticAnalysisCommon.isFunctionDefinition', function (t) {
+ t.plan(3)
+ var node1 = { name: 'FunctionDefinition' }
+ var node2 = { name: 'MemberAccess' }
+ var node3 = { name: 'FunctionDefinitionBLABLA' }
+
+ t.ok(common.isFunctionDefinition(node1), 'is exact match should work')
+ t.notOk(common.isFunctionDefinition(node2), 'different node should not work')
+ t.notOk(common.isFunctionDefinition(node3), 'substring should not work')
+})
+
+test('staticAnalysisCommon.isModifierDefinition', function (t) {
+ t.plan(3)
+ var node1 = { name: 'ModifierDefinition' }
+ var node2 = { name: 'MemberAccess' }
+ var node3 = { name: 'ModifierDefinitionBLABLA' }
+
+ t.ok(common.isModifierDefinition(node1), 'is exact match should work')
+ t.notOk(common.isModifierDefinition(node2), 'different node should not work')
+ t.notOk(common.isModifierDefinition(node3), 'substring should not work')
+})
+
+test('staticAnalysisCommon.isModifierInvocation', function (t) {
+ t.plan(3)
+ var node1 = { name: 'ModifierInvocation' }
+ var node2 = { name: 'MemberAccess' }
+ var node3 = { name: 'ModifierInvocationBLABLA' }
+
+ t.ok(common.isModifierInvocation(node1), 'is exact match should work')
+ t.notOk(common.isModifierInvocation(node2), 'different node should not work')
+ t.notOk(common.isModifierInvocation(node3), 'substring should not work')
+})
+
+test('staticAnalysisCommon.isVariableDeclaration', function (t) {
+ t.plan(3)
+ var node1 = { name: 'VariableDeclaration' }
+ var node2 = { name: 'MemberAccess' }
+ var node3 = { name: 'VariableDeclarationBLABLA' }
+
+ t.ok(common.isVariableDeclaration(node1), 'is exact match should work')
+ t.notOk(common.isVariableDeclaration(node2), 'different node should not work')
+ t.notOk(common.isVariableDeclaration(node3), 'substring should not work')
+})
+
+test('staticAnalysisCommon.isInheritanceSpecifier', function (t) {
+ t.plan(3)
+ var node1 = { name: 'InheritanceSpecifier' }
+ var node2 = { name: 'MemberAccess' }
+ var node3 = { name: 'InheritanceSpecifierBLABLA' }
+
+ t.ok(common.isInheritanceSpecifier(node1), 'is exact match should work')
+ t.notOk(common.isInheritanceSpecifier(node2), 'different node should not work')
+ t.notOk(common.isInheritanceSpecifier(node3), 'substring should not work')
+})
+
+test('staticAnalysisCommon.isAssignment', function (t) {
+ t.plan(3)
+ var node1 = { name: 'Assignment' }
+ var node2 = { name: 'MemberAccess' }
+ var node3 = { name: 'AssignmentBLABLA' }
+
+ t.ok(common.isAssignment(node1), 'is exact match should work')
+ t.notOk(common.isAssignment(node2), 'different node should not work')
+ t.notOk(common.isAssignment(node3), 'substring should not work')
+})
+
+test('staticAnalysisCommon.isContractDefinition', function (t) {
+ t.plan(3)
+ var node1 = { name: 'ContractDefinition' }
+ var node2 = { name: 'MemberAccess' }
+ var node3 = { name: 'ContractDefinitionBLABLA' }
+
+ t.ok(common.isContractDefinition(node1), 'is exact match should work')
+ t.notOk(common.isContractDefinition(node2), 'different node should not work')
+ t.notOk(common.isContractDefinition(node3), 'substring should not work')
+})
+
+test('staticAnalysisCommon.isInlineAssembly', function (t) {
+ t.plan(3)
+ var node1 = { name: 'InlineAssembly' }
+ var node2 = { name: 'MemberAccess' }
+ var node3 = { name: 'InlineAssemblyBLABLA' }
+
+ t.ok(common.isInlineAssembly(node1), 'is exact match should work')
+ t.notOk(common.isInlineAssembly(node2), 'different node should not work')
+ t.notOk(common.isInlineAssembly(node3), 'substring should not work')
+})
+
+// #################### Complex Node Identification
+
+test('staticAnalysisCommon.isBuiltinFunctionCall', function (t) {
+ t.plan(2)
+ var selfdestruct = {
+ 'attributes': {
+ 'type': 'tuple()',
+ 'type_conversion': false
+ },
+ 'children': [
+ {
+ 'attributes': {
+ 'type': 'function (address)',
+ 'value': 'selfdestruct'
+ },
+ 'name': 'Identifier'
+ },
+ {
+ 'attributes': {
+ 'type': 'address',
+ 'value': 'a'
+ },
+ 'name': 'Identifier'
+ }
+ ],
+ 'name': 'FunctionCall'
+ }
+ var localCall = {
+ 'attributes': {
+ 'type': 'tuple()',
+ 'type_conversion': false
+ },
+ 'children': [
+ {
+ 'attributes': {
+ 'type': 'function (struct Ballot.Voter storage pointer)',
+ 'value': 'bli'
+ },
+ 'name': 'Identifier'
+ },
+ {
+ 'attributes': {
+ 'type': 'struct Ballot.Voter storage pointer',
+ 'value': 'x'
+ },
+ 'name': 'Identifier'
+ }
+ ],
+ 'name': 'FunctionCall'
+ }
+
+ t.ok(common.isBuiltinFunctionCall(selfdestruct), 'selfdestruct is builtin')
+ t.notOk(common.isBuiltinFunctionCall(localCall), 'local call is not builtin')
+})
+
+test('staticAnalysisCommon.isStorageVariableDeclaration', function (t) {
+ t.plan(3)
+ var node1 = {
+ 'attributes': {
+ 'name': 'x',
+ 'type': 'struct Ballot.Voter storage pointer'
+ },
+ 'children': [
+ {
+ 'attributes': {
+ 'name': 'Voter'
+ },
+ 'id': 43,
+ 'name': 'UserDefinedTypeName',
+ 'src': '604:5:0'
+ }
+ ],
+ 'id': 44,
+ 'name': 'VariableDeclaration',
+ 'src': '604:15:0'
+ }
+ var node2 = {
+ 'attributes': {
+ 'name': 'voters',
+ 'type': 'mapping(address => struct Ballot.Voter storage ref)'
+ },
+ 'children': [
+ {
+ 'children': [
+ {
+ 'attributes': {
+ 'name': 'address'
+ },
+ 'id': 16,
+ 'name': 'ElementaryTypeName',
+ 'src': '235:7:0'
+ },
+ {
+ 'attributes': {
+ 'name': 'Voter'
+ },
+ 'id': 17,
+ 'name': 'UserDefinedTypeName',
+ 'src': '246:5:0'
+ }
+ ],
+ 'id': 18,
+ 'name': 'Mapping',
+ 'src': '227:25:0'
+ }
+ ],
+ 'id': 19,
+ 'name': 'VariableDeclaration',
+ 'src': '227:32:0'
+ }
+ var node3 = {
+ 'attributes': {
+ 'name': 'voters',
+ 'type': 'bytes32'
+ },
+ 'children': [
+ {
+ 'attributes': {
+ 'name': 'bytes'
+ },
+ 'id': 16,
+ 'name': 'ElementaryTypeName',
+ 'src': '235:7:0'
+ }
+ ],
+ 'id': 19,
+ 'name': 'VariableDeclaration',
+ 'src': '227:32:0'
+ }
+
+ t.ok(common.isStorageVariableDeclaration(node1), 'struct storage pointer param is storage')
+ t.ok(common.isStorageVariableDeclaration(node2), 'struct storage pointer mapping param is storage')
+ t.notOk(common.isStorageVariableDeclaration(node3), 'bytes is not storage')
+})
+
+test('staticAnalysisCommon.isInteraction', function (t) {
+ t.plan(6)
+ var sendAst = { name: 'MemberAccess', children: [{attributes: { value: 'd', type: 'address' }}], attributes: { value: 'send', type: 'function (uint256) returns (bool)' } }
+ var callAst = { name: 'MemberAccess', children: [{attributes: { value: 'f', type: 'address' }}], attributes: { member_name: 'call', type: 'function () payable returns (bool)' } }
+ var callcodeAst = { name: 'MemberAccess', children: [{attributes: { value: 'f', type: 'address' }}], attributes: { member_name: 'callcode', type: 'function () payable returns (bool)' } }
+ var delegatecallAst = { name: 'MemberAccess', children: [{attributes: { value: 'g', type: 'address' }}], attributes: { member_name: 'delegatecall', type: 'function () returns (bool)' } }
+ var nodeExtDir = {
+ attributes: {
+ member_name: 'info',
+ type: 'function () payable external returns (uint256)'
+ },
+ children: [
+ {
+ attributes: {
+ type: 'contract InfoFeed',
+ value: 'f'
+ },
+ id: 30,
+ name: 'Identifier',
+ src: '405:1:0'
+ }
+ ],
+ id: 32,
+ name: 'MemberAccess',
+ src: '405:6:0'
+ }
+ var nodeNot = {
+ 'attributes': {
+ 'type': 'tuple()',
+ 'type_conversion': false
+ },
+ 'children': [
+ {
+ 'attributes': {
+ 'type': 'function (struct Ballot.Voter storage pointer)',
+ 'value': 'bli'
+ },
+ 'id': 37,
+ 'name': 'Identifier',
+ 'src': '540:3:0'
+ },
+ {
+ 'attributes': {
+ 'type': 'struct Ballot.Voter storage pointer',
+ 'value': 'x'
+ },
+ 'id': 38,
+ 'name': 'Identifier',
+ 'src': '544:1:0'
+ }
+ ],
+ 'id': 39,
+ 'name': 'FunctionCall',
+ 'src': '540:6:0'
+ }
+
+ t.ok(common.isInteraction(sendAst), 'send is interaction')
+ t.ok(common.isInteraction(callAst), 'call is interaction')
+ t.ok(common.isInteraction(nodeExtDir), 'ExternalDirecCall is interaction')
+ t.notOk(common.isInteraction(callcodeAst), 'callcode is not interaction')
+ t.notOk(common.isInteraction(delegatecallAst), 'callcode is not interaction')
+ t.notOk(common.isInteraction(nodeNot), 'local call is not interaction')
+})
+
+test('staticAnalysisCommon.isEffect', function (t) {
+ t.plan(5)
+ var inlineAssembly = {
+ 'children': [
+ ],
+ 'id': 21,
+ 'name': 'InlineAssembly',
+ 'src': '809:41:0'
+ }
+ var assignment = {
+ 'attributes': {
+ 'operator': '=',
+ 'type': 'uint256'
+ },
+ 'children': [
+ {
+ 'attributes': {
+ 'type': 'uint256'
+ },
+ 'children': [
+ {
+ 'attributes': {
+ 'type': 'mapping(address => uint256)',
+ 'value': 'c'
+ },
+ 'id': 61,
+ 'name': 'Identifier',
+ 'src': '873:1:0'
+ },
+ {
+ 'attributes': {
+ 'member_name': 'sender',
+ 'type': 'address'
+ },
+ 'children': [
+ {
+ 'attributes': {
+ 'type': 'msg',
+ 'value': 'msg'
+ },
+ 'id': 62,
+ 'name': 'Identifier',
+ 'src': '875:3:0'
+ }
+ ],
+ 'id': 63,
+ 'name': 'MemberAccess',
+ 'src': '875:10:0'
+ }
+ ],
+ 'id': 64,
+ 'name': 'IndexAccess',
+ 'src': '873:13:0'
+ },
+ {
+ 'attributes': {
+ 'hexvalue': '30',
+ 'subdenomination': null,
+ 'token': null,
+ 'type': 'int_const 0',
+ 'value': '0'
+ },
+ 'id': 65,
+ 'name': 'Literal',
+ 'src': '889:1:0'
+ }
+ ],
+ 'id': 66,
+ 'name': 'Assignment',
+ 'src': '873:17:0'
+ }
+ var unaryOp = { name: 'UnaryOperation', attributes: { operator: '++' } }
+ t.ok(common.isEffect(inlineAssembly), 'inline assembly is treated as effect')
+ t.ok(common.isEffect(assignment), 'assignment is treated as effect')
+ t.ok(common.isEffect(unaryOp), '++ is treated as effect')
+ unaryOp.attributes.operator = '--'
+ t.ok(common.isEffect(unaryOp), '-- is treated as effect')
+ t.notOk(common.isEffect({ name: 'MemberAccess', attributes: { operator: '++' } }), 'MemberAccess not treated as effect')
+})
+
+test('staticAnalysisCommon.isWriteOnStateVariable', function (t) {
+ t.plan(3)
+ var inlineAssembly = {
+ 'children': [
+ ],
+ 'id': 21,
+ 'name': 'InlineAssembly',
+ 'src': '809:41:0'
+ }
+ var assignment = {
+ 'attributes': {
+ 'operator': '=',
+ 'type': 'uint256'
+ },
+ 'children': [
+ {
+ 'attributes': {
+ 'type': 'uint256'
+ },
+ 'children': [
+ {
+ 'attributes': {
+ 'type': 'mapping(address => uint256)',
+ 'value': 'c'
+ },
+ 'id': 61,
+ 'name': 'Identifier',
+ 'src': '873:1:0'
+ },
+ {
+ 'attributes': {
+ 'member_name': 'sender',
+ 'type': 'address'
+ },
+ 'children': [
+ {
+ 'attributes': {
+ 'type': 'msg',
+ 'value': 'msg'
+ },
+ 'id': 62,
+ 'name': 'Identifier',
+ 'src': '875:3:0'
+ }
+ ],
+ 'id': 63,
+ 'name': 'MemberAccess',
+ 'src': '875:10:0'
+ }
+ ],
+ 'id': 64,
+ 'name': 'IndexAccess',
+ 'src': '873:13:0'
+ },
+ {
+ 'attributes': {
+ 'hexvalue': '30',
+ 'subdenomination': null,
+ 'token': null,
+ 'type': 'int_const 0',
+ 'value': '0'
+ },
+ 'id': 65,
+ 'name': 'Literal',
+ 'src': '889:1:0'
+ }
+ ],
+ 'id': 66,
+ 'name': 'Assignment',
+ 'src': '873:17:0'
+ }
+ var node1 = {
+ 'attributes': {
+ 'name': 'x',
+ 'type': 'struct Ballot.Voter storage pointer'
+ },
+ 'children': [
+ {
+ 'attributes': {
+ 'name': 'Voter'
+ },
+ 'name': 'UserDefinedTypeName'
+ }
+ ],
+ 'name': 'VariableDeclaration'
+ }
+ var node2 = {
+ 'attributes': {
+ 'name': 'y',
+ 'type': 'uint'
+ },
+ 'children': [
+ {
+ 'attributes': {
+ 'name': 'Voter'
+ },
+ 'name': 'UserDefinedTypeName'
+ }
+ ],
+ 'name': 'VariableDeclaration'
+ }
+ var node3 = {
+ 'attributes': {
+ 'name': 'xx',
+ 'type': 'uint'
+ },
+ 'children': [
+ {
+ 'attributes': {
+ 'name': 'Voter'
+ },
+ 'name': 'UserDefinedTypeName'
+ }
+ ],
+ 'name': 'VariableDeclaration'
+ }
+ t.ok(common.isWriteOnStateVariable(inlineAssembly, [node1, node2, node3]), 'inline Assembly is write on state')
+ t.notOk(common.isWriteOnStateVariable(assignment, [node1, node2, node3]), 'assignment on non state is not write on state')
+ node3.attributes.name = 'c'
+ t.ok(common.isWriteOnStateVariable(assignment, [node1, node2, node3]), 'assignment on state is not write on state')
+})
+
+test('staticAnalysisCommon.isStateVariable', function (t) {
+ t.plan(3)
+ var node1 = {
+ 'attributes': {
+ 'name': 'x',
+ 'type': 'struct Ballot.Voter storage pointer'
+ },
+ 'children': [
+ {
+ 'attributes': {
+ 'name': 'Voter'
+ },
+ 'name': 'UserDefinedTypeName'
+ }
+ ],
+ 'name': 'VariableDeclaration'
+ }
+ var node2 = {
+ 'attributes': {
+ 'name': 'y',
+ 'type': 'uint'
+ },
+ 'children': [
+ {
+ 'attributes': {
+ 'name': 'Voter'
+ },
+ 'name': 'UserDefinedTypeName'
+ }
+ ],
+ 'name': 'VariableDeclaration'
+ }
+ var node3 = {
+ 'attributes': {
+ 'name': 'xx',
+ 'type': 'uint'
+ },
+ 'children': [
+ {
+ 'attributes': {
+ 'name': 'Voter'
+ },
+ 'name': 'UserDefinedTypeName'
+ }
+ ],
+ 'name': 'VariableDeclaration'
+ }
+
+ t.ok(common.isStateVariable('x', [node1, node2]), 'is contained')
+ t.ok(common.isStateVariable('x', [node2, node1, node1]), 'is contained twice')
+ t.notOk(common.isStateVariable('x', [node2, node3]), 'not contained')
+})
+
+test('staticAnalysisCommon.isConstantFunction', function (t) {
+ t.plan(3)
+ var node1 = { name: 'FunctionDefinition', attributes: { constant: true } }
+ var node2 = { name: 'FunctionDefinition', attributes: { constant: false } }
+ var node3 = { name: 'MemberAccess', attributes: { constant: true } }
+
+ t.ok(common.isConstantFunction(node1), 'should be const func definition')
+ t.notOk(common.isConstantFunction(node2), 'should not be const func definition')
+ t.notOk(common.isConstantFunction(node3), 'wrong node should not be const func definition')
+})
+
+test('staticAnalysisCommon.isPlusPlusUnaryOperation', function (t) {
+ t.plan(3)
+ var node1 = { name: 'UnaryOperation', attributes: { operator: '++' } }
+ var node2 = { name: 'UnaryOperation', attributes: { operator: '--' } }
+ var node3 = { name: 'FunctionDefinition', attributes: { operator: '++' } }
+
+ t.ok(common.isPlusPlusUnaryOperation(node1), 'should be unary ++')
+ t.notOk(common.isPlusPlusUnaryOperation(node2), 'should not be unary ++')
+ t.notOk(common.isPlusPlusUnaryOperation(node3), 'wrong node should not be unary ++')
+})
+
+test('staticAnalysisCommon.isMinusMinusUnaryOperation', function (t) {
+ t.plan(3)
+ var node1 = { name: 'UnaryOperation', attributes: { operator: '--' } }
+ var node2 = { name: 'UnaryOperation', attributes: { operator: '++' } }
+ var node3 = { name: 'FunctionDefinition', attributes: { operator: '--' } }
+
+ t.ok(common.isMinusMinusUnaryOperation(node1), 'should be unary --')
+ t.notOk(common.isMinusMinusUnaryOperation(node2), 'should not be unary --')
+ t.notOk(common.isMinusMinusUnaryOperation(node3), 'wrong node should not be unary --')
+})
+
+test('staticAnalysisCommon.isFullyImplementedContract', function (t) {
+ t.plan(3)
+ var node1 = { name: 'ContractDefinition', attributes: { fullyImplemented: true } }
+ var node2 = { name: 'ContractDefinition', attributes: { fullyImplemented: false } }
+ var node3 = { name: 'FunctionDefinition', attributes: { operator: '--' } }
+
+ t.ok(common.isFullyImplementedContract(node1), 'should be fully implemented contract')
+ t.notOk(common.isFullyImplementedContract(node2), 'should not be fully implemented contract')
+ t.notOk(common.isFullyImplementedContract(node3), 'wrong node should not be fully implemented contract')
+})
+
+test('staticAnalysisCommon.isCallToNonConstLocalFunction', function (t) {
+ t.plan(2)
+ var node1 = {
+ 'attributes': {
+ 'type': 'tuple()',
+ 'type_conversion': false
+ },
+ 'children': [
+ {
+ 'attributes': {
+ 'type': 'function (struct Ballot.Voter storage pointer)',
+ 'value': 'bli'
+ },
+ 'name': 'Identifier'
+ },
+ {
+ 'attributes': {
+ 'type': 'struct Ballot.Voter storage pointer',
+ 'value': 'x'
+ },
+ 'name': 'Identifier'
+ }
+ ],
+ 'name': 'FunctionCall'
+ }
+
+ t.ok(common.isCallToNonConstLocalFunction(node1), 'should be call to non const Local func')
+ node1.children[0].attributes.type = 'function (struct Ballot.Voter storage pointer) constant payable (uint256)'
+ t.notok(common.isCallToNonConstLocalFunction(node1), 'should no longer be call to non const Local func')
+})
+
+test('staticAnalysisCommon.isExternalDirectCall', function (t) {
+ t.plan(5)
+ var node = {
+ attributes: {
+ member_name: 'info',
+ type: 'function () payable external returns (uint256)'
+ },
+ children: [
+ {
+ attributes: {
+ type: 'contract InfoFeed',
+ value: 'f'
+ },
+ id: 30,
+ name: 'Identifier',
+ src: '405:1:0'
+ }
+ ],
+ id: 32,
+ name: 'MemberAccess',
+ src: '405:6:0'
+ }
+
+ var node2 = { name: 'MemberAccess', children: [{attributes: { value: 'this', type: 'contract test' }}], attributes: { value: 'b', type: 'function (bytes32,address) returns (bool)' } }
+ t.notOk(common.isThisLocalCall(node), 'is this.local_method() used should not work')
+ t.notOk(common.isBlockTimestampAccess(node), 'is block.timestamp used should not work')
+ t.notOk(common.isNowAccess(node), 'is now used should not work')
+ t.ok(common.isExternalDirectCall(node), 'f.info() should be external direct call')
+ t.notOk(common.isExternalDirectCall(node2), 'local call is not an exernal call')
+})
+
+test('staticAnalysisCommon.isNowAccess', function (t) {
+ t.plan(3)
+ var node = { name: 'Identifier', attributes: { value: 'now', type: 'uint256' } }
+ t.notOk(common.isThisLocalCall(node), 'is this.local_method() used should not work')
+ t.notOk(common.isBlockTimestampAccess(node), 'is block.timestamp used should not work')
+ t.ok(common.isNowAccess(node), 'is now used should work')
+})
+
+test('staticAnalysisCommon.isBlockTimestampAccess', function (t) {
+ t.plan(3)
+ var node = { name: 'MemberAccess', children: [{attributes: { value: 'block', type: 'block' }}], attributes: { value: 'timestamp', type: 'uint256' } }
+ t.notOk(common.isThisLocalCall(node), 'is this.local_method() used should not work')
+ t.ok(common.isBlockTimestampAccess(node), 'is block.timestamp used should work')
+ t.notOk(common.isNowAccess(node), 'is now used should not work')
+})
+
+test('staticAnalysisCommon.isBlockBlockhashAccess', function (t) {
+ t.plan(4)
+ var node = {
+ 'attributes': {
+ 'member_name': 'blockhash',
+ 'type': 'function (uint256) returns (bytes32)'
+ },
+ 'children': [
+ {
+ 'attributes': {
+ 'type': 'block',
+ 'value': 'block'
+ },
+ 'name': 'Identifier'
+ }
+ ],
+ 'name': 'MemberAccess'
+ }
+
+ t.notOk(common.isThisLocalCall(node), 'is this.local_method() used should not work')
+ t.notOk(common.isBlockTimestampAccess(node), 'is block.timestamp used should not work')
+ t.ok(common.isBlockBlockHashAccess(node), 'blockhash should work') // todo:
+ t.notOk(common.isNowAccess(node), 'is now used should not work')
+})
+
+test('staticAnalysisCommon.isThisLocalCall', function (t) {
+ t.plan(3)
+ var node = { name: 'MemberAccess', children: [{attributes: { value: 'this', type: 'contract test' }}], attributes: { value: 'b', type: 'function (bytes32,address) returns (bool)' } }
+ t.ok(common.isThisLocalCall(node), 'is this.local_method() used should work')
+ t.notOk(common.isBlockTimestampAccess(node), 'is block.timestamp used should not work')
+ t.notOk(common.isNowAccess(node), 'is now used should not work')
+})
+
+test('staticAnalysisCommon.isSuperLocalCall', function (t) {
+ t.plan(4)
+ var node = {
+ 'attributes': {
+ 'member_name': 'duper',
+ 'type': 'function ()'
+ },
+ 'children': [
+ {
+ 'attributes': {
+ 'type': 'contract super a',
+ 'value': 'super'
+ },
+ 'name': 'Identifier'
+ }
+ ],
+ 'name': 'MemberAccess'
+ }
+ t.ok(common.isSuperLocalCall(node), 'is super.local_method() used should work')
+ t.notOk(common.isThisLocalCall(node), 'is this.local_method() used should not work')
+ t.notOk(common.isBlockTimestampAccess(node), 'is block.timestamp used should not work')
+ t.notOk(common.isNowAccess(node), 'is now used should not work')
+})
+
+test('staticAnalysisCommon.isLibraryCall', function (t) {
+ t.plan(5)
+ var node = {
+ 'attributes': {
+ 'member_name': 'insert',
+ 'type': 'function (struct Set.Data storage pointer,uint256) returns (bool)'
+ },
+ 'children': [
+ {
+ 'attributes': {
+ 'type': 'type(library Set)',
+ 'value': 'Set'
+ },
+ 'name': 'Identifier'
+ }
+ ],
+ 'name': 'MemberAccess'
+ }
+ t.ok(common.isLibraryCall(node), 'is lib call should not work')
+ t.notOk(common.isSuperLocalCall(node), 'is super.local_method() used should not work')
+ t.notOk(common.isThisLocalCall(node), 'is this.local_method() used should not work')
+ t.notOk(common.isBlockTimestampAccess(node), 'is block.timestamp used should not work')
+ t.notOk(common.isNowAccess(node), 'is now used should not work')
+})
+
+test('staticAnalysisCommon.isLocalCall', function (t) {
+ t.plan(5)
+ var node1 = {
+ 'attributes': {
+ 'type': 'tuple()',
+ 'type_conversion': false
+ },
+ 'children': [
+ {
+ 'attributes': {
+ 'type': 'function (struct Ballot.Voter storage pointer)',
+ 'value': 'bli'
+ },
+ 'id': 37,
+ 'name': 'Identifier',
+ 'src': '540:3:0'
+ },
+ {
+ 'attributes': {
+ 'type': 'struct Ballot.Voter storage pointer',
+ 'value': 'x'
+ },
+ 'id': 38,
+ 'name': 'Identifier',
+ 'src': '544:1:0'
+ }
+ ],
+ 'id': 39,
+ 'name': 'FunctionCall',
+ 'src': '540:6:0'
+ }
+
+ t.ok(common.isLocalCall(node1), 'isLocalCall')
+ t.notOk(common.isLowLevelCall(node1), 'is not low level call')
+ t.notOk(common.isExternalDirectCall(node1), 'is not external direct call')
+ t.notOk(common.isEffect(node1), 'is not effect')
+ t.notOk(common.isInteraction(node1), 'is not interaction')
+})
+
+test('staticAnalysisCommon.isLowLevelCall', function (t) {
+ t.plan(6)
+ var sendAst = { name: 'MemberAccess', children: [{attributes: { value: 'd', type: 'address' }}], attributes: { value: 'send', type: 'function (uint256) returns (bool)' } }
+ var callAst = { name: 'MemberAccess', children: [{attributes: { value: 'f', type: 'address' }}], attributes: { member_name: 'call', type: 'function () payable returns (bool)' } }
+ var callcodeAst = { name: 'MemberAccess', children: [{attributes: { value: 'f', type: 'address' }}], attributes: { member_name: 'callcode', type: 'function () payable returns (bool)' } }
+ var delegatecallAst = { name: 'MemberAccess', children: [{attributes: { value: 'g', type: 'address' }}], attributes: { member_name: 'delegatecall', type: 'function () returns (bool)' } }
+
+ t.ok(common.isLowLevelSendInst(sendAst) && common.isLowLevelCall(sendAst), 'send is llc should work')
+ t.ok(common.isLowLevelCallInst(callAst) && common.isLowLevelCall(callAst), 'call is llc should work')
+ t.notOk(common.isLowLevelCallInst(callcodeAst), 'callcode is not call')
+ t.ok(common.isLowLevelCallcodeInst(callcodeAst) && common.isLowLevelCall(callcodeAst), 'callcode is llc should work')
+ t.notOk(common.isLowLevelCallcodeInst(callAst), 'call is not callcode')
+ t.ok(common.isLowLevelDelegatecallInst(delegatecallAst) && common.isLowLevelCall(delegatecallAst), 'delegatecall is llc should work')
+})
+
+test('staticAnalysisCommon: Call of parameter function', function (t) {
+ t.plan(7)
+ var node1 = {
+ 'attributes': {
+ 'argumentTypes': null,
+ 'isConstant': false,
+ 'isLValue': false,
+ 'isPure': false,
+ 'isStructConstructorCall': false,
+ 'lValueRequested': false,
+ 'names': [
+ null
+ ],
+ 'type': 'uint256',
+ 'type_conversion': false
+ },
+ 'children': [
+ {
+ 'attributes': {
+ 'argumentTypes': [
+ {
+ 'typeIdentifier': 't_uint256',
+ 'typeString': 'uint256'
+ },
+ {
+ 'typeIdentifier': 't_uint256',
+ 'typeString': 'uint256'
+ }
+ ],
+ 'overloadedDeclarations': [
+ null
+ ],
+ 'referencedDeclaration': 25,
+ 'type': 'function (uint256,uint256) pure returns (uint256)',
+ 'value': 'f'
+ },
+ 'id': 34,
+ 'name': 'Identifier',
+ 'src': '267:1:0'
+ },
+ {
+ 'attributes': {
+ 'argumentTypes': null,
+ 'overloadedDeclarations': [
+ null
+ ],
+ 'referencedDeclaration': 27,
+ 'type': 'uint256',
+ 'value': 'x'
+ },
+ 'id': 35,
+ 'name': 'Identifier',
+ 'src': '269:1:0'
+ },
+ {
+ 'attributes': {
+ 'argumentTypes': null,
+ 'overloadedDeclarations': [
+ null
+ ],
+ 'referencedDeclaration': 29,
+ 'type': 'uint256',
+ 'value': 'y'
+ },
+ 'id': 36,
+ 'name': 'Identifier',
+ 'src': '272:1:0'
+ }
+ ],
+ 'id': 37,
+ 'name': 'FunctionCall',
+ 'src': '267:7:0'
+ }
+
+ t.ok(common.isLocalCall(node1), 'is not LocalCall')
+ t.notOk(common.isThisLocalCall(node1), 'is not this local call')
+ t.notOk(common.isSuperLocalCall(node1), 'is not super local call')
+ t.notOk(common.isExternalDirectCall(node1), 'is not ExternalDirectCall')
+ t.notOk(common.isLibraryCall(node1), 'is not LibraryCall')
+
+ t.equals(common.getFunctionCallType(node1), 'function (uint256,uint256) pure returns (uint256)', 'Extracts right type')
+
+ t.equals(common.getFunctionCallTypeParameterType(node1), 'uint256,uint256', 'Extracts param right type')
+})
+
+test('staticAnalysisCommon: function call with of function with function parameter', function (t) {
+ t.plan(2)
+ var node1 = {
+ 'attributes': {
+ 'argumentTypes': null,
+ 'isConstant': false,
+ 'isLValue': false,
+ 'isPure': false,
+ 'isStructConstructorCall': false,
+ 'lValueRequested': false,
+ 'names': [
+ null
+ ],
+ 'type': 'uint256',
+ 'type_conversion': false
+ },
+ 'children': [
+ {
+ 'attributes': {
+ 'argumentTypes': [
+ {
+ 'typeIdentifier': 't_function_internal_pure$_t_uint256_$_t_uint256_$returns$_t_uint256_$',
+ 'typeString': 'function (uint256,uint256) pure returns (uint256)'
+ },
+ {
+ 'typeIdentifier': 't_uint256',
+ 'typeString': 'uint256'
+ },
+ {
+ 'typeIdentifier': 't_uint256',
+ 'typeString': 'uint256'
+ }
+ ],
+ 'overloadedDeclarations': [
+ null
+ ],
+ 'referencedDeclaration': 40,
+ 'type': 'function (function (uint256,uint256) pure returns (uint256),uint256,uint256) pure returns (uint256)',
+ 'value': 'eval'
+ },
+ 'id': 49,
+ 'name': 'Identifier',
+ 'src': '361:4:0'
+ },
+ {
+ 'attributes': {
+ 'argumentTypes': null,
+ 'overloadedDeclarations': [
+ null
+ ],
+ 'referencedDeclaration': 15,
+ 'type': 'function (uint256,uint256) pure returns (uint256)',
+ 'value': 'plus'
+ },
+ 'id': 50,
+ 'name': 'Identifier',
+ 'src': '366:4:0'
+ },
+ {
+ 'attributes': {
+ 'argumentTypes': null,
+ 'overloadedDeclarations': [
+ null
+ ],
+ 'referencedDeclaration': 42,
+ 'type': 'uint256',
+ 'value': 'x'
+ },
+ 'id': 51,
+ 'name': 'Identifier',
+ 'src': '372:1:0'
+ },
+ {
+ 'attributes': {
+ 'argumentTypes': null,
+ 'overloadedDeclarations': [
+ null
+ ],
+ 'referencedDeclaration': 44,
+ 'type': 'uint256',
+ 'value': 'y'
+ },
+ 'id': 52,
+ 'name': 'Identifier',
+ 'src': '375:1:0'
+ }
+ ],
+ 'id': 53,
+ 'name': 'FunctionCall',
+ 'src': '361:16:0'
+ }
+
+ t.equals(common.getFunctionCallType(node1), 'function (function (uint256,uint256) pure returns (uint256),uint256,uint256) pure returns (uint256)', 'Extracts right type')
+
+ t.equals(common.getFunctionCallTypeParameterType(node1), 'function (uint256,uint256) pure returns (uint256),uint256,uint256', 'Extracts param right type')
+})
+
+test('staticAnalysisCommon: require call', function (t) {
+ t.plan(3)
+ var node = {'attributes': {'argumentTypes': null, 'isConstant': false, 'isLValue': false, 'isPure': false, 'isStructConstructorCall': false, 'lValueRequested': false, 'names': [null], 'type': 'tuple()', 'type_conversion': false}, 'children': [{'attributes': {'argumentTypes': [{'typeIdentifier': 't_bool', 'typeString': 'bool'}, {'typeIdentifier': 't_stringliteral_80efd193f332877914d93edb0b3ef5c6a7eecd00c6251c3fd7f146b60b40e6cd', 'typeString': 'literal_string \'fuu\''}], 'overloadedDeclarations': [90, 91], 'referencedDeclaration': 91, 'type': 'function (bool,string memory) pure', 'value': 'require'}, 'id': 50, 'name': 'Identifier', 'src': '462:7:0'}, {'attributes': {'argumentTypes': null, 'commonType': {'typeIdentifier': 't_address', 'typeString': 'address'}, 'isConstant': false, 'isLValue': false, 'isPure': false, 'lValueRequested': false, 'operator': '==', 'type': 'bool'}, 'children': [{'attributes': {'argumentTypes': null, 'isConstant': false, 'isLValue': false, 'isPure': false, 'lValueRequested': false, 'member_name': 'sender', 'referencedDeclaration': null, 'type': 'address'}, 'children': [{'attributes': {'argumentTypes': null, 'overloadedDeclarations': [null], 'referencedDeclaration': 87, 'type': 'msg', 'value': 'msg'}, 'id': 51, 'name': 'Identifier', 'src': '470:3:0'}], 'id': 52, 'name': 'MemberAccess', 'src': '470:10:0'}, {'attributes': {'argumentTypes': null, 'overloadedDeclarations': [null], 'referencedDeclaration': 10, 'type': 'address', 'value': 'owner'}, 'id': 53, 'name': 'Identifier', 'src': '484:5:0'}], 'id': 54, 'name': 'BinaryOperation', 'src': '470:19:0'}, {'attributes': {'argumentTypes': null, 'hexvalue': '667575', 'isConstant': false, 'isLValue': false, 'isPure': true, 'lValueRequested': false, 'subdenomination': null, 'token': 'string', 'type': 'literal_string \'fuu\'', 'value': 'fuu'}, 'id': 55, 'name': 'Literal', 'src': '491:5:0'}], 'id': 56, 'name': 'FunctionCall', 'src': '462:35:0'}
+
+ t.equals(common.isRequireCall(node), true)
+ t.equals(common.getFunctionCallType(node), 'function (bool,string memory) pure', 'Extracts right type')
+ t.equals(common.getFunctionCallTypeParameterType(node), 'bool,string memory', 'Extracts param right type')
+})
+
+test('staticAnalysisCommon: isDeleteOfDynamicArray', function (t) {
+ t.plan(2)
+ var node = {'attributes': {'argumentTypes': null, 'isConstant': false, 'isLValue': false, 'isPure': false, 'lValueRequested': false, 'operator': 'delete', 'prefix': true, 'type': 'tuple()'}, 'children': [{'attributes': {'argumentTypes': null, 'overloadedDeclarations': [null], 'referencedDeclaration': 4, 'type': 'uint256[] storage ref', 'value': 'users'}, 'id': 58, 'name': 'Identifier', 'src': '514:5:0'}], 'id': 59, 'name': 'UnaryOperation', 'src': '507:12:0'}
+ t.equals(common.isDeleteOfDynamicArray(node), true)
+ t.equals(common.isDynamicArrayAccess(node.children[0]), true, 'Extracts right type')
+})
+
+test('staticAnalysisCommon: isAbiNamespaceCall', function (t) {
+ t.plan(8)
+ var node1 = {'attributes': {'argumentTypes': null, 'isConstant': false, 'isLValue': false, 'isPure': false, 'isStructConstructorCall': false, 'lValueRequested': false, 'names': [null], 'type': 'bytes memory', 'type_conversion': false}, 'children': [{'attributes': {'argumentTypes': [{'typeIdentifier': 't_uint256', 'typeString': 'uint256'}, {'typeIdentifier': 't_uint256', 'typeString': 'uint256'}], 'isConstant': false, 'isLValue': false, 'isPure': false, 'lValueRequested': false, 'member_name': 'encode', 'referencedDeclaration': null, 'type': 'function () pure returns (bytes memory)'}, 'children': [{'attributes': {'argumentTypes': null, 'overloadedDeclarations': [null], 'referencedDeclaration': 64, 'type': 'abi', 'value': 'abi'}, 'id': 26, 'name': 'Identifier', 'src': '245: 3:0'}], 'id': 28, 'name': 'MemberAccess', 'src': '245:10:0'}, {'attributes': {'argumentTypes': null, 'overloadedDeclarations': [null], 'referencedDeclaration': 7, 'type': 'uint256', 'value': 'a'}, 'id': 29, 'name': 'Identifier', 'src': '256:1:0'}, {'attributes': {'argumentTypes': null, 'overloadedDeclarations': [null], 'referencedDeclaration': 15, 'type': 'uint256', 'value': 'b'}, 'id': 30, 'name': 'Identifier', 'src': '258:1:0'}], 'id': 31, 'name': 'FunctionCall', 'src': '245:15:0'}
+ var node2 = {'attributes': {'argumentTypes': null, 'isConstant': false, 'isLValue': false, 'isPure': false, 'isStructConstructorCall': false, 'lValueRequested': false, 'names': [null], 'type': 'bytes memory', 'type_conversion': false}, 'children': [{'attributes': {'argumentTypes': [{'typeIdentifier': 't_uint256', 'typeString': 'uint256'}, {'typeIdentifier': 't_uint256', 'typeString': 'uint256'}], 'isConstant': false, 'isLValue': false, 'isPure': false, 'lValueRequested': false, 'member_name': 'encodePacked', 'referencedDeclaration': null, 'type': 'function () pure returns (bytes memory)'}, 'children': [{'attributes': {'argumentTypes': null, 'overloadedDeclarations': [null], 'referencedDeclaration': 64, 'type': 'abi', 'value': 'abi'}, 'id': 33, 'name': 'Identifier', 'src': '279:3:0'}], 'id': 35, 'name': 'MemberAccess', 'src': '279:16:0'}, {'attributes': {'argumentTypes': null, 'overloadedDeclarations': [null], 'referencedDeclaration': 7, 'type': 'uint256', 'value': 'a'}, 'id': 36, 'name': 'Identifier', 'src': '296:1:0'}, {'attributes': {'argumentTypes': null, 'overloadedDeclarations': [null], 'referencedDeclaration': 15, 'type': 'uint256', 'value': 'b'}, 'id': 37, 'name': 'Identifier', 'src': '298:1:0'}], 'id': 38, 'name': 'FunctionCall', 'src': '279:21:0'}
+ var node3 = {'attributes': {'argumentTypes': null, 'isConstant': false, 'isLValue': false, 'isPure': false, 'isStructConstructorCall': false, 'lValueRequested': false, 'names': [null], 'type': 'bytes memory', 'type_conversion': false}, 'children': [{'attributes': {'argumentTypes': [{'typeIdentifier': 't_bytes4', 'typeString': 'bytes4'}, {'typeIdentifier': 't_uint256', 'typeString': 'uint256'}, {'typeIdentifier': 't_uint256', 'typeString': 'uint256'}], 'isConstant': false, 'isLValue': false, 'isPure': false, 'lValueRequested': false, 'member_name': 'encodeWithSelector', 'referencedDeclaration': null, 'type': 'function (bytes4) pure returns (bytes memory)'}, 'children': [{'attributes': {'argumentTypes': null, 'overloadedDeclarations': [null], 'referencedDeclaration': 64, 'type': 'abi', 'value': 'abi'}, 'id': 40, 'name': 'Identifier', 'src': '319:3:0'}], 'id': 42, 'name': 'MemberAccess', 'src': '319:22:0'}, {'attributes': {'argumentTypes': null, 'overloadedDeclarations': [null], 'referencedDeclaration': 19, 'type': 'bytes4', 'value': 'selector'}, 'id': 43, 'name': 'Identifier', 'src': '342:8:0'}, {'attributes': {'argumentTypes': null, 'overloadedDeclarations': [null], 'referencedDeclaration': 7, 'type': 'uint256', 'value': 'a'}, 'id': 44, 'name': 'Identifier', 'src': '352:1:0'}, {'attributes': {'argumentTypes': null, 'overloadedDeclarations': [null], 'referencedDeclaration': 15, 'type': 'uint256', 'value': 'b'}, 'id': 45, 'name': 'Identifier', 'src': '355:1:0'}], 'id': 46, 'name': 'FunctionCall', 'src': '319:38:0'}
+ var node4 = {'attributes': {'argumentTypes': null, 'isConstant': false, 'isLValue': false, 'isPure': false, 'isStructConstructorCall': false, 'lValueRequested': false, 'names': [null], 'type': 'bytes memory', 'type_conversion': false}, 'children': [{'attributes': {'argumentTypes': [{'typeIdentifier': 't_string_memory_ptr', 'typeString': 'string memory'}, {'typeIdentifier': 't_uint256', 'typeString': 'uint256'}, {'typeIdentifier': 't_uint256', 'typeString': 'uint256'}], 'isConstant': false, 'isLValue': false, 'isPure': false, 'lValueRequested': false, 'member_name': 'encodeWithSignature', 'referencedDeclaration': null, 'type': 'function (string memory) pure returns (bytes memory)'}, 'children': [{'attributes': {'argumentTypes': null, 'overloadedDeclarations': [null], 'referencedDeclaration': 64, 'type': 'abi', 'value': 'abi'}, 'id': 48, 'name': 'Identifier', 'src': '367:3:0'}], 'id': 50, 'name': 'MemberAccess', 'src': '367:23:0'}, {'attributes': {'argumentTypes': null, 'overloadedDeclarations': [null], 'referencedDeclaration': 11, 'type': 'string memory', 'value': 'sig'}, 'id': 51, 'name': 'Identifier', 'src': '391:3:0'}, {'attributes': {'argumentTypes': null, 'overloadedDeclarations': [null], 'referencedDeclaration': 7, 'type': 'uint256', 'value': 'a'}, 'id': 52, 'name': 'Identifier', 'src': '396:1:0'}, {'attributes': {'argumentTypes': null, 'overloadedDeclarations': [null], 'referencedDeclaration': 15, 'type': 'uint256', 'value': 'b'}, 'id': 53, 'name': 'Identifier', 'src': '399:1:0'}], 'id': 54, 'name': 'FunctionCall', 'src': '367:34:0'}
+
+ t.equals(common.isAbiNamespaceCall(node1), true, 'encode abi')
+ t.equals(common.isAbiNamespaceCall(node2), true, 'encodePacked abi')
+ t.equals(common.isAbiNamespaceCall(node3), true, 'encodeWithSelector abi')
+ t.equals(common.isAbiNamespaceCall(node4), true, 'encodeWithSignature abi')
+
+ t.equals(common.isBuiltinFunctionCall(node1), true, 'encode Builtin')
+ t.equals(common.isBuiltinFunctionCall(node2), true, 'encodePacked Builtin')
+ t.equals(common.isBuiltinFunctionCall(node3), true, 'encodeWithSelector Builtin')
+ t.equals(common.isBuiltinFunctionCall(node4), true, 'encodeWithSignature Builtin')
+})
diff --git a/remix-analyzer/test/analysis/staticAnalysisIntegration-test.js b/remix-analyzer/test/analysis/staticAnalysisIntegration-test.js
new file mode 100644
index 0000000000..3b1f31308a
--- /dev/null
+++ b/remix-analyzer/test/analysis/staticAnalysisIntegration-test.js
@@ -0,0 +1,634 @@
+var test = require('tape')
+var remixLib = require('remix-lib')
+
+var StatRunner = require('../../src/analysis/staticAnalysisRunner')
+var compilerInput = remixLib.helpers.compiler.compilerInput
+
+var compiler = require('solc')
+
+var fs = require('fs')
+var path = require('path')
+
+var testFiles = [
+ 'KingOfTheEtherThrone.sol',
+ 'assembly.sol',
+ 'ballot.sol',
+ 'ballot_reentrant.sol',
+ 'ballot_withoutWarnings.sol',
+ 'cross_contract.sol',
+ 'inheritance.sol',
+ 'modifier1.sol',
+ 'modifier2.sol',
+ 'notReentrant.sol',
+ 'structReentrant.sol',
+ 'thisLocal.sol',
+ 'globals.sol',
+ 'library.sol',
+ 'transfer.sol',
+ 'ctor.sol',
+ 'forgottenReturn.sol',
+ 'selfdestruct.sol',
+ 'deleteDynamicArray.sol',
+ 'blockLevelCompare.sol',
+ 'intDivisionTruncate.sol'
+]
+
+var testFileAsts = {}
+
+testFiles.forEach((fileName) => {
+ var content = fs.readFileSync(path.join(__dirname, 'test-contracts', fileName), 'utf8')
+ testFileAsts[fileName] = JSON.parse(compiler.compileStandardWrapper(compilerInput(content)))
+})
+
+test('Integration test thisLocal.js', function (t) {
+ t.plan(testFiles.length)
+
+ var module = require('../../src/analysis/modules/thisLocal')
+
+ var lengthCheck = {
+ 'KingOfTheEtherThrone.sol': 0,
+ 'assembly.sol': 0,
+ 'ballot.sol': 0,
+ 'ballot_reentrant.sol': 1,
+ 'ballot_withoutWarnings.sol': 0,
+ 'cross_contract.sol': 0,
+ 'inheritance.sol': 0,
+ 'modifier1.sol': 0,
+ 'modifier2.sol': 0,
+ 'notReentrant.sol': 0,
+ 'structReentrant.sol': 0,
+ 'thisLocal.sol': 1,
+ 'globals.sol': 0,
+ 'library.sol': 0,
+ 'transfer.sol': 0,
+ 'ctor.sol': 0,
+ 'forgottenReturn.sol': 0,
+ 'selfdestruct.sol': 0,
+ 'deleteDynamicArray.sol': 0,
+ 'blockLevelCompare.sol': 0,
+ 'intDivisionTruncate.sol': 0
+ }
+
+ runModuleOnFiles(module, t, (file, report) => {
+ t.equal(report.length, lengthCheck[file], `${file} has right amount of this local warnings`)
+ })
+})
+
+test('Integration test checksEffectsInteraction.js', function (t) {
+ t.plan(testFiles.length)
+
+ var module = require('../../src/analysis/modules/checksEffectsInteraction')
+
+ var lengthCheck = {
+ 'KingOfTheEtherThrone.sol': 1,
+ 'assembly.sol': 1,
+ 'ballot.sol': 0,
+ 'ballot_reentrant.sol': 1,
+ 'ballot_withoutWarnings.sol': 0,
+ 'cross_contract.sol': 0,
+ 'inheritance.sol': 1,
+ 'modifier1.sol': 0,
+ 'modifier2.sol': 0,
+ 'notReentrant.sol': 0,
+ 'structReentrant.sol': 1,
+ 'thisLocal.sol': 0,
+ 'globals.sol': 1,
+ 'library.sol': 1,
+ 'transfer.sol': 1,
+ 'ctor.sol': 0,
+ 'forgottenReturn.sol': 0,
+ 'selfdestruct.sol': 0,
+ 'deleteDynamicArray.sol': 0,
+ 'blockLevelCompare.sol': 0,
+ 'intDivisionTruncate.sol': 0
+ }
+
+ runModuleOnFiles(module, t, (file, report) => {
+ t.equal(report.length, lengthCheck[file], `${file} has right amount of checks-effects-interaction warnings`)
+ })
+})
+
+test('Integration test constantFunctions.js', function (t) {
+ t.plan(testFiles.length)
+
+ var module = require('../../src/analysis/modules/constantFunctions')
+
+ var lengthCheck = {
+ 'KingOfTheEtherThrone.sol': 0,
+ 'assembly.sol': 0,
+ 'ballot.sol': 0,
+ 'ballot_reentrant.sol': 0,
+ 'ballot_withoutWarnings.sol': 0,
+ 'cross_contract.sol': 1,
+ 'inheritance.sol': 0,
+ 'modifier1.sol': 1,
+ 'modifier2.sol': 0,
+ 'notReentrant.sol': 0,
+ 'structReentrant.sol': 1,
+ 'thisLocal.sol': 1,
+ 'globals.sol': 0,
+ 'library.sol': 3,
+ 'transfer.sol': 0,
+ 'ctor.sol': 0,
+ 'forgottenReturn.sol': 0,
+ 'selfdestruct.sol': 1,
+ 'deleteDynamicArray.sol': 0,
+ 'blockLevelCompare.sol': 0,
+ 'intDivisionTruncate.sol': 0
+ }
+
+ runModuleOnFiles(module, t, (file, report) => {
+ t.equal(report.length, lengthCheck[file], `${file} has right amount of constant warnings`)
+ })
+})
+
+test('Integration test inlineAssembly.js', function (t) {
+ t.plan(testFiles.length)
+
+ var module = require('../../src/analysis/modules/inlineAssembly')
+
+ var lengthCheck = {
+ 'KingOfTheEtherThrone.sol': 0,
+ 'assembly.sol': 2,
+ 'ballot.sol': 0,
+ 'ballot_reentrant.sol': 0,
+ 'ballot_withoutWarnings.sol': 0,
+ 'cross_contract.sol': 0,
+ 'inheritance.sol': 0,
+ 'modifier1.sol': 0,
+ 'modifier2.sol': 0,
+ 'notReentrant.sol': 0,
+ 'structReentrant.sol': 0,
+ 'thisLocal.sol': 0,
+ 'globals.sol': 0,
+ 'library.sol': 0,
+ 'transfer.sol': 0,
+ 'ctor.sol': 0,
+ 'forgottenReturn.sol': 0,
+ 'selfdestruct.sol': 0,
+ 'deleteDynamicArray.sol': 0,
+ 'blockLevelCompare.sol': 0,
+ 'intDivisionTruncate.sol': 0
+ }
+
+ runModuleOnFiles(module, t, (file, report) => {
+ t.equal(report.length, lengthCheck[file], `${file} has right amount of inline assembly warnings`)
+ })
+})
+
+test('Integration test txOrigin.js', function (t) {
+ t.plan(testFiles.length)
+
+ var module = require('../../src/analysis/modules/txOrigin')
+
+ var lengthCheck = {
+ 'KingOfTheEtherThrone.sol': 0,
+ 'assembly.sol': 1,
+ 'ballot.sol': 0,
+ 'ballot_reentrant.sol': 0,
+ 'ballot_withoutWarnings.sol': 0,
+ 'cross_contract.sol': 0,
+ 'inheritance.sol': 0,
+ 'modifier1.sol': 0,
+ 'modifier2.sol': 0,
+ 'notReentrant.sol': 0,
+ 'structReentrant.sol': 0,
+ 'thisLocal.sol': 0,
+ 'globals.sol': 1,
+ 'library.sol': 0,
+ 'transfer.sol': 0,
+ 'ctor.sol': 0,
+ 'forgottenReturn.sol': 0,
+ 'selfdestruct.sol': 0,
+ 'deleteDynamicArray.sol': 0,
+ 'blockLevelCompare.sol': 0,
+ 'intDivisionTruncate.sol': 0
+ }
+
+ runModuleOnFiles(module, t, (file, report) => {
+ t.equal(report.length, lengthCheck[file], `${file} has right amount of tx.origin warnings`)
+ })
+})
+
+test('Integration test gasCosts.js', function (t) {
+ t.plan(testFiles.length)
+
+ var module = require('../../src/analysis/modules/gasCosts')
+
+ var lengthCheck = {
+ 'KingOfTheEtherThrone.sol': 2,
+ 'assembly.sol': 2,
+ 'ballot.sol': 3,
+ 'ballot_reentrant.sol': 2,
+ 'ballot_withoutWarnings.sol': 0,
+ 'cross_contract.sol': 1,
+ 'inheritance.sol': 1,
+ 'modifier1.sol': 0,
+ 'modifier2.sol': 1,
+ 'notReentrant.sol': 1,
+ 'structReentrant.sol': 1,
+ 'thisLocal.sol': 1,
+ 'globals.sol': 1,
+ 'library.sol': 1,
+ 'transfer.sol': 1,
+ 'ctor.sol': 0,
+ 'forgottenReturn.sol': 3,
+ 'selfdestruct.sol': 0,
+ 'deleteDynamicArray.sol': 2,
+ 'blockLevelCompare.sol': 1,
+ 'intDivisionTruncate.sol': 1
+ }
+
+ runModuleOnFiles(module, t, (file, report) => {
+ t.equal(report.length, lengthCheck[file], `${file} has right amount of gasCost warnings`)
+ })
+})
+
+test('Integration test similarVariableNames.js', function (t) {
+ t.plan(testFiles.length)
+
+ var module = require('../../src/analysis/modules/similarVariableNames')
+
+ var lengthCheck = {
+ 'KingOfTheEtherThrone.sol': 0,
+ 'assembly.sol': 0,
+ 'ballot.sol': 2,
+ 'ballot_reentrant.sol': 3,
+ 'ballot_withoutWarnings.sol': 0,
+ 'cross_contract.sol': 0,
+ 'inheritance.sol': 0,
+ 'modifier1.sol': 0,
+ 'modifier2.sol': 0,
+ 'notReentrant.sol': 1,
+ 'structReentrant.sol': 0,
+ 'thisLocal.sol': 0,
+ 'globals.sol': 0,
+ 'library.sol': 0,
+ 'transfer.sol': 0,
+ 'ctor.sol': 1,
+ 'forgottenReturn.sol': 0,
+ 'selfdestruct.sol': 0,
+ 'deleteDynamicArray.sol': 1,
+ 'blockLevelCompare.sol': 0,
+ 'intDivisionTruncate.sol': 0
+ }
+
+ runModuleOnFiles(module, t, (file, report) => {
+ t.equal(report.length, lengthCheck[file], `${file} has right amount of similarVariableNames warnings`)
+ })
+})
+
+test('Integration test inlineAssembly.js', function (t) {
+ t.plan(testFiles.length)
+
+ var module = require('../../src/analysis/modules/inlineAssembly')
+
+ var lengthCheck = {
+ 'KingOfTheEtherThrone.sol': 0,
+ 'assembly.sol': 2,
+ 'ballot.sol': 0,
+ 'ballot_reentrant.sol': 0,
+ 'ballot_withoutWarnings.sol': 0,
+ 'cross_contract.sol': 0,
+ 'inheritance.sol': 0,
+ 'modifier1.sol': 0,
+ 'modifier2.sol': 0,
+ 'notReentrant.sol': 0,
+ 'structReentrant.sol': 0,
+ 'thisLocal.sol': 0,
+ 'globals.sol': 0,
+ 'library.sol': 0,
+ 'transfer.sol': 0,
+ 'ctor.sol': 0,
+ 'forgottenReturn.sol': 0,
+ 'selfdestruct.sol': 0,
+ 'deleteDynamicArray.sol': 0,
+ 'blockLevelCompare.sol': 0,
+ 'intDivisionTruncate.sol': 0
+ }
+
+ runModuleOnFiles(module, t, (file, report) => {
+ t.equal(report.length, lengthCheck[file], `${file} has right amount of inlineAssembly warnings`)
+ })
+})
+
+test('Integration test blockTimestamp.js', function (t) {
+ t.plan(testFiles.length)
+
+ var module = require('../../src/analysis/modules/blockTimestamp')
+
+ var lengthCheck = {
+ 'KingOfTheEtherThrone.sol': 1,
+ 'assembly.sol': 0,
+ 'ballot.sol': 0,
+ 'ballot_reentrant.sol': 3,
+ 'ballot_withoutWarnings.sol': 0,
+ 'cross_contract.sol': 0,
+ 'inheritance.sol': 0,
+ 'modifier1.sol': 0,
+ 'modifier2.sol': 0,
+ 'notReentrant.sol': 0,
+ 'structReentrant.sol': 0,
+ 'thisLocal.sol': 0,
+ 'globals.sol': 2,
+ 'library.sol': 0,
+ 'transfer.sol': 0,
+ 'ctor.sol': 0,
+ 'forgottenReturn.sol': 0,
+ 'selfdestruct.sol': 0,
+ 'deleteDynamicArray.sol': 0,
+ 'blockLevelCompare.sol': 0,
+ 'intDivisionTruncate.sol': 0
+ }
+
+ runModuleOnFiles(module, t, (file, report) => {
+ t.equal(report.length, lengthCheck[file], `${file} has right amount of blockTimestamp warnings`)
+ })
+})
+
+test('Integration test lowLevelCalls.js', function (t) {
+ t.plan(testFiles.length)
+
+ var module = require('../../src/analysis/modules/lowLevelCalls')
+
+ var lengthCheck = {
+ 'KingOfTheEtherThrone.sol': 1,
+ 'assembly.sol': 1,
+ 'ballot.sol': 0,
+ 'ballot_reentrant.sol': 7,
+ 'ballot_withoutWarnings.sol': 0,
+ 'cross_contract.sol': 1,
+ 'inheritance.sol': 1,
+ 'modifier1.sol': 0,
+ 'modifier2.sol': 0,
+ 'notReentrant.sol': 1,
+ 'structReentrant.sol': 1,
+ 'thisLocal.sol': 2,
+ 'globals.sol': 1,
+ 'library.sol': 1,
+ 'transfer.sol': 0,
+ 'ctor.sol': 0,
+ 'forgottenReturn.sol': 0,
+ 'selfdestruct.sol': 0,
+ 'deleteDynamicArray.sol': 0,
+ 'blockLevelCompare.sol': 0,
+ 'intDivisionTruncate.sol': 0
+ }
+
+ runModuleOnFiles(module, t, (file, report) => {
+ t.equal(report.length, lengthCheck[file], `${file} has right amount of lowLevelCalls warnings`)
+ })
+})
+
+test('Integration test blockBlockhash.js', function (t) {
+ t.plan(testFiles.length)
+
+ var module = require('../../src/analysis/modules/blockBlockhash')
+
+ var lengthCheck = {
+ 'KingOfTheEtherThrone.sol': 0,
+ 'assembly.sol': 0,
+ 'ballot.sol': 0,
+ 'ballot_reentrant.sol': 0,
+ 'ballot_withoutWarnings.sol': 0,
+ 'cross_contract.sol': 0,
+ 'inheritance.sol': 0,
+ 'modifier1.sol': 0,
+ 'modifier2.sol': 0,
+ 'notReentrant.sol': 0,
+ 'structReentrant.sol': 0,
+ 'thisLocal.sol': 0,
+ 'globals.sol': 0, // was 1 !! @TODO
+ 'library.sol': 0,
+ 'transfer.sol': 0,
+ 'ctor.sol': 0,
+ 'forgottenReturn.sol': 0,
+ 'selfdestruct.sol': 0,
+ 'deleteDynamicArray.sol': 0,
+ 'blockLevelCompare.sol': 0,
+ 'intDivisionTruncate.sol': 0
+ }
+
+ runModuleOnFiles(module, t, (file, report) => {
+ t.equal(report.length, lengthCheck[file], `${file} has right amount of blockBlockhash warnings`)
+ })
+})
+
+test('Integration test noReturn.js', function (t) {
+ t.plan(testFiles.length)
+
+ var module = require('../../src/analysis/modules/noReturn')
+
+ var lengthCheck = {
+ 'KingOfTheEtherThrone.sol': 0,
+ 'assembly.sol': 1,
+ 'ballot.sol': 0,
+ 'ballot_reentrant.sol': 0,
+ 'ballot_withoutWarnings.sol': 0,
+ 'cross_contract.sol': 0,
+ 'inheritance.sol': 0,
+ 'modifier1.sol': 1,
+ 'modifier2.sol': 0,
+ 'notReentrant.sol': 0,
+ 'structReentrant.sol': 0,
+ 'thisLocal.sol': 1,
+ 'globals.sol': 0,
+ 'library.sol': 0,
+ 'transfer.sol': 0,
+ 'ctor.sol': 0,
+ 'forgottenReturn.sol': 1,
+ 'selfdestruct.sol': 0,
+ 'deleteDynamicArray.sol': 0,
+ 'blockLevelCompare.sol': 0,
+ 'intDivisionTruncate.sol': 0
+ }
+
+ runModuleOnFiles(module, t, (file, report) => {
+ t.equal(report.length, lengthCheck[file], `${file} has right amount of noReturn warnings`)
+ })
+})
+
+test('Integration test selfdestruct.js', function (t) {
+ t.plan(testFiles.length)
+
+ var module = require('../../src/analysis/modules/selfdestruct')
+
+ var lengthCheck = {
+ 'KingOfTheEtherThrone.sol': 0,
+ 'assembly.sol': 0,
+ 'ballot.sol': 0,
+ 'ballot_reentrant.sol': 0,
+ 'ballot_withoutWarnings.sol': 0,
+ 'cross_contract.sol': 0,
+ 'inheritance.sol': 0,
+ 'modifier1.sol': 0,
+ 'modifier2.sol': 0,
+ 'notReentrant.sol': 0,
+ 'structReentrant.sol': 0,
+ 'thisLocal.sol': 0,
+ 'globals.sol': 1,
+ 'library.sol': 0,
+ 'transfer.sol': 0,
+ 'ctor.sol': 0,
+ 'forgottenReturn.sol': 0,
+ 'selfdestruct.sol': 2,
+ 'deleteDynamicArray.sol': 0,
+ 'blockLevelCompare.sol': 0,
+ 'intDivisionTruncate.sol': 1
+ }
+
+ runModuleOnFiles(module, t, (file, report) => {
+ t.equal(report.length, lengthCheck[file], `${file} has right amount of selfdestruct warnings`)
+ })
+})
+
+test('Integration test guardConditions.js', function (t) {
+ t.plan(testFiles.length)
+
+ var module = require('../../src/analysis/modules/guardConditions')
+
+ var lengthCheck = {
+ 'KingOfTheEtherThrone.sol': 0,
+ 'assembly.sol': 1,
+ 'ballot.sol': 0,
+ 'ballot_reentrant.sol': 0,
+ 'ballot_withoutWarnings.sol': 0,
+ 'cross_contract.sol': 0,
+ 'inheritance.sol': 0,
+ 'modifier1.sol': 0,
+ 'modifier2.sol': 0,
+ 'notReentrant.sol': 0,
+ 'structReentrant.sol': 0,
+ 'thisLocal.sol': 0,
+ 'globals.sol': 1,
+ 'library.sol': 0,
+ 'transfer.sol': 0,
+ 'ctor.sol': 0,
+ 'forgottenReturn.sol': 0,
+ 'selfdestruct.sol': 0,
+ 'deleteDynamicArray.sol': 1,
+ 'blockLevelCompare.sol': 0,
+ 'intDivisionTruncate.sol': 1
+ }
+
+ runModuleOnFiles(module, t, (file, report) => {
+ t.equal(report.length, lengthCheck[file], `${file} has right amount of guardCondition warnings`)
+ })
+})
+
+test('Integration test deleteDynamicArrays.js', function (t) {
+ t.plan(testFiles.length)
+
+ var module = require('../../src/analysis/modules/deleteDynamicArrays')
+
+ var lengthCheck = {
+ 'KingOfTheEtherThrone.sol': 0,
+ 'assembly.sol': 0,
+ 'ballot.sol': 0,
+ 'ballot_reentrant.sol': 0,
+ 'ballot_withoutWarnings.sol': 0,
+ 'cross_contract.sol': 0,
+ 'inheritance.sol': 0,
+ 'modifier1.sol': 0,
+ 'modifier2.sol': 0,
+ 'notReentrant.sol': 0,
+ 'structReentrant.sol': 0,
+ 'thisLocal.sol': 0,
+ 'globals.sol': 0,
+ 'library.sol': 0,
+ 'transfer.sol': 0,
+ 'ctor.sol': 0,
+ 'forgottenReturn.sol': 0,
+ 'selfdestruct.sol': 0,
+ 'deleteDynamicArray.sol': 2,
+ 'blockLevelCompare.sol': 0,
+ 'intDivisionTruncate.sol': 0
+ }
+
+ runModuleOnFiles(module, t, (file, report) => {
+ t.equal(report.length, lengthCheck[file], `${file} has right amount of deleteDynamicArrays warnings`)
+ })
+})
+
+test('Integration test assignAndCompare.js', function (t) {
+ t.plan(testFiles.length)
+
+ var module = require('../../src/analysis/modules/assignAndCompare')
+
+ var lengthCheck = {
+ 'KingOfTheEtherThrone.sol': 0,
+ 'assembly.sol': 0,
+ 'ballot.sol': 0,
+ 'ballot_reentrant.sol': 0,
+ 'ballot_withoutWarnings.sol': 0,
+ 'cross_contract.sol': 0,
+ 'inheritance.sol': 0,
+ 'modifier1.sol': 0,
+ 'modifier2.sol': 0,
+ 'notReentrant.sol': 0,
+ 'structReentrant.sol': 0,
+ 'thisLocal.sol': 0,
+ 'globals.sol': 0,
+ 'library.sol': 0,
+ 'transfer.sol': 0,
+ 'ctor.sol': 0,
+ 'forgottenReturn.sol': 0,
+ 'selfdestruct.sol': 0,
+ 'deleteDynamicArray.sol': 0,
+ 'blockLevelCompare.sol': 8,
+ 'intDivisionTruncate.sol': 0
+ }
+
+ runModuleOnFiles(module, t, (file, report) => {
+ t.equal(report.length, lengthCheck[file], `${file} has right amount of assignAndCompare warnings`)
+ })
+})
+
+test('Integration test intDivisionTruncate.js', function (t) {
+ t.plan(testFiles.length)
+
+ var module = require('../../src/analysis/modules/intDivisionTruncate')
+
+ var lengthCheck = {
+ 'KingOfTheEtherThrone.sol': 0,
+ 'assembly.sol': 0,
+ 'ballot.sol': 0,
+ 'ballot_reentrant.sol': 0,
+ 'ballot_withoutWarnings.sol': 0,
+ 'cross_contract.sol': 0,
+ 'inheritance.sol': 0,
+ 'modifier1.sol': 0,
+ 'modifier2.sol': 0,
+ 'notReentrant.sol': 0,
+ 'structReentrant.sol': 0,
+ 'thisLocal.sol': 0,
+ 'globals.sol': 0,
+ 'library.sol': 0,
+ 'transfer.sol': 0,
+ 'ctor.sol': 0,
+ 'forgottenReturn.sol': 0,
+ 'selfdestruct.sol': 0,
+ 'deleteDynamicArray.sol': 0,
+ 'blockLevelCompare.sol': 0,
+ 'intDivisionTruncate.sol': 2
+ }
+
+ runModuleOnFiles(module, t, (file, report) => {
+ t.equal(report.length, lengthCheck[file], `${file} has right amount of intDivisionTruncate warnings`)
+ })
+})
+
+// #################### Helpers
+function runModuleOnFiles (module, t, cb) {
+ var statRunner = new StatRunner()
+
+ testFiles.forEach((fileName) => {
+ statRunner.runWithModuleList(testFileAsts[fileName], [{ name: module.name, mod: new module.Module() }], (reports) => {
+ let report = reports[0].report
+ if (report.some((x) => x['warning'].includes('INTERNAL ERROR'))) {
+ t.comment('Error while executing Module: ' + JSON.stringify(report))
+ }
+ cb(fileName, report)
+ })
+ })
+}
diff --git a/remix-analyzer/test/analysis/staticAnalysisIssues-test.js b/remix-analyzer/test/analysis/staticAnalysisIssues-test.js
new file mode 100644
index 0000000000..eb7b97a2ef
--- /dev/null
+++ b/remix-analyzer/test/analysis/staticAnalysisIssues-test.js
@@ -0,0 +1,34 @@
+var test = require('tape')
+var remixLib = require('remix-lib')
+
+var StatRunner = require('../../src/analysis/staticAnalysisRunner')
+var compilerInput = remixLib.helpers.compiler.compilerInput
+
+var compiler = require('solc')
+
+var fs = require('fs')
+var path = require('path')
+
+function compile (fileName) {
+ var content = fs.readFileSync(path.join(__dirname, 'test-contracts', fileName), 'utf8')
+ return JSON.parse(compiler.compileStandardWrapper(compilerInput(content)))
+}
+
+test('staticAnalysisIssues.functionParameterPassingError', function (t) {
+ // https://github.com/ethereum/remix-ide/issues/889#issuecomment-351746474
+ t.plan(2)
+ var res = compile('functionParameters.sol')
+
+ var module = require('../../src/analysis/modules/checksEffectsInteraction')
+
+ var statRunner = new StatRunner()
+
+ t.doesNotThrow(() => {
+ statRunner.runWithModuleList(res, [{ name: module.name, mod: new module.Module() }], (reports) => {
+ })
+ }, true, 'Analysis should not throw')
+
+ statRunner.runWithModuleList(res, [{ name: module.name, mod: new module.Module() }], (reports) => {
+ t.ok(!reports.some((mod) => mod.report.some((rep) => rep.warning.includes('INTERNAL ERROR')), 'Should not have internal errors'))
+ })
+})
diff --git a/remix-analyzer/test/analysis/test-contracts/KingOfTheEtherThrone.sol b/remix-analyzer/test/analysis/test-contracts/KingOfTheEtherThrone.sol
new file mode 100644
index 0000000000..70cfb08327
--- /dev/null
+++ b/remix-analyzer/test/analysis/test-contracts/KingOfTheEtherThrone.sol
@@ -0,0 +1,23 @@
+// return value send
+contract KingOfTheEtherThrone{
+ struct Monarch {
+ // address of the king .
+ address ethAddr ;
+ string name ;
+ // how much he pays to previous king
+ uint claimPrice ;
+ uint coronationTimestamp;
+ }
+ Monarch public currentMonarch ;
+
+ function claimThrone ( string name ) {
+ address wizardAddress;
+ uint compensation = 100;
+ uint valuePaid = 10;
+
+ if ( currentMonarch.ethAddr != wizardAddress )
+ if (currentMonarch.ethAddr.send( compensation )) throw;
+
+ currentMonarch = Monarch(msg.sender,name,valuePaid,block.timestamp);
+ }
+}
diff --git a/remix-analyzer/test/analysis/test-contracts/assembly.sol b/remix-analyzer/test/analysis/test-contracts/assembly.sol
new file mode 100644
index 0000000000..fb647502eb
--- /dev/null
+++ b/remix-analyzer/test/analysis/test-contracts/assembly.sol
@@ -0,0 +1,30 @@
+ pragma solidity ^0.4.9;
+ contract test {
+
+ address owner;
+
+ function at(address _addr) returns (bytes o_code) {
+ assert(_addr != 0x0);
+ assembly {
+ // retrieve the size of the code, this needs assembly
+ let size := extcodesize(_addr)
+ // allocate output byte array - this could also be done without assembly
+ // by using o_code = new bytes(size)
+ o_code := mload(0x40)
+ // new "memory end" including padding
+ mstore(0x40, add(o_code, and(add(add(size, 0x20), 0x1f), not(0x1f))))
+ // store length in memory
+ mstore(o_code, size)
+ // actually retrieve the code, this needs assembly
+ extcodecopy(_addr, add(o_code, 0x20), 0, size)
+ }
+ }
+
+ function bla() {
+ require(tx.origin == owner);
+ msg.sender.send(19);
+ assembly {
+
+ }
+ }
+ }
diff --git a/remix-analyzer/test/analysis/test-contracts/ballot.sol b/remix-analyzer/test/analysis/test-contracts/ballot.sol
new file mode 100644
index 0000000000..e1e9f676fe
--- /dev/null
+++ b/remix-analyzer/test/analysis/test-contracts/ballot.sol
@@ -0,0 +1,145 @@
+pragma solidity ^0.4.0;
+
+/// @title Voting with delegation.
+contract Ballot {
+ // This declares a new complex type which will
+ // be used for variables later.
+ // It will represent a single voter.
+ struct Voter {
+ uint weight; // weight is accumulated by delegation
+ bool voted; // if true, that person already voted
+ address delegate; // person delegated to
+ uint vote; // index of the voted proposal
+ }
+
+ // This is a type for a single proposal.
+ struct Proposal
+ {
+ bytes32 name; // short name (up to 32 bytes)
+ uint voteCount; // number of accumulated votes
+ }
+
+ address public chairperson;
+
+ // This declares a state variable that
+ // stores a `Voter` struct for each possible address.
+ mapping(address => Voter) public voters;
+
+ // A dynamically-sized array of `Proposal` structs.
+ Proposal[] public proposals;
+
+ /// Create a new ballot to choose one of `proposalNames`.
+ function Ballot(bytes32[] proposalNames) {
+ chairperson = msg.sender;
+ voters[chairperson].weight = 1;
+
+ // For each of the provided proposal names,
+ // create a new proposal object and add it
+ // to the end of the array.
+ for (uint i = 0; i < proposalNames.length; i++) {
+ // `Proposal({...})` creates a temporary
+ // Proposal object and `proposals.push(...)`
+ // appends it to the end of `proposals`.
+ proposals.push(Proposal({
+ name: proposalNames[i],
+ voteCount: 0
+ }));
+ }
+ }
+
+ // Give `voter` the right to vote on this ballot.
+ // May only be called by `chairperson`.
+ function giveRightToVote(address voter) {
+ if (msg.sender != chairperson || voters[voter].voted) {
+ // `throw` terminates and reverts all changes to
+ // the state and to Ether balances. It is often
+ // a good idea to use this if functions are
+ // called incorrectly. But watch out, this
+ // will also consume all provided gas.
+ throw;
+ }
+ voters[voter].weight = 1;
+ }
+
+ /// Delegate your vote to the voter `to`.
+ function delegate(address to) {
+ // assigns reference
+ Voter sender = voters[msg.sender];
+ if (sender.voted)
+ throw;
+
+ // Forward the delegation as long as
+ // `to` also delegated.
+ // In general, such loops are very dangerous,
+ // because if they run too long, they might
+ // need more gas than is available in a block.
+ // In this case, the delegation will not be executed,
+ // but in other situations, such loops might
+ // cause a contract to get "stuck" completely.
+ while (
+ voters[to].delegate != address(0) &&
+ voters[to].delegate != msg.sender
+ ) {
+ to = voters[to].delegate;
+ }
+
+ // We found a loop in the delegation, not allowed.
+ if (to == msg.sender) {
+ throw;
+ }
+
+ // Since `sender` is a reference, this
+ // modifies `voters[msg.sender].voted`
+ sender.voted = true;
+ sender.delegate = to;
+ Voter delegate = voters[to];
+ if (delegate.voted) {
+ // If the delegate already voted,
+ // directly add to the number of votes
+ proposals[delegate.vote].voteCount += sender.weight;
+ } else {
+ // If the delegate did not vote yet,
+ // add to her weight.
+ delegate.weight += sender.weight;
+ }
+ }
+
+ /// Give your vote (including votes delegated to you)
+ /// to proposal `proposals[proposal].name`.
+ function vote(uint proposal) {
+ Voter sender = voters[msg.sender];
+ if (sender.voted)
+ throw;
+ sender.voted = true;
+ sender.vote = proposal;
+
+ // If `proposal` is out of the range of the array,
+ // this will throw automatically and revert all
+ // changes.
+ proposals[proposal].voteCount += sender.weight;
+ }
+
+ /// @dev Computes the winning proposal taking all
+ /// previous votes into account.
+ function winningProposal() constant
+ returns (uint winningProposal)
+ {
+ uint winningVoteCount = 0;
+ for (uint p = 0; p < proposals.length; p++) {
+ if (proposals[p].voteCount > winningVoteCount) {
+ winningVoteCount = proposals[p].voteCount;
+ winningProposal = p;
+ }
+ }
+ }
+
+ // Calls winningProposal() function to get the index
+ // of the winner contained in the proposals array and then
+ // returns the name of the winner
+ function winnerName() constant
+ returns (bytes32 winnerName)
+ {
+ winnerName = proposals[winningProposal()].name;
+ }
+}
+
diff --git a/remix-analyzer/test/analysis/test-contracts/ballot_reentrant.sol b/remix-analyzer/test/analysis/test-contracts/ballot_reentrant.sol
new file mode 100644
index 0000000000..a695b09e19
--- /dev/null
+++ b/remix-analyzer/test/analysis/test-contracts/ballot_reentrant.sol
@@ -0,0 +1,101 @@
+pragma solidity ^0.4.0;
+
+contract InfoFeed {
+ function info() payable returns (uint ret);
+ function call1(uint a) payable returns (bool);
+}
+
+
+contract Ballot {
+
+ InfoFeed feed;
+
+ struct Voter {
+ uint weight;
+ bool voted;
+ uint8 vote;
+ address delegate;
+ }
+ struct Proposal {
+ uint voteCount;
+ }
+
+ address chairperson;
+ mapping(address => Voter) voters;
+ Proposal[] proposals;
+
+ function send1(address a) {
+ giveRightToVote(a,a);
+ }
+
+ /// Create a new ballot with $(_numProposals) different proposals.
+ function Ballot(uint8 _numProposals) {
+ address d;
+ if(!d.delegatecall.gas(800)('function_name', 'arg1', 'arg2')) throw;
+ if(!d.callcode.gas(800)('function_name', 'arg1', 'arg2')) throw;
+ if(!d.call.value(10).gas(800)('function_name', 'arg1', 'arg2')) throw;
+ if(!d.call.value(10).gas(800)('function_name', 'arg1', 'arg2')) throw;
+
+
+
+ if(!msg.sender.send(1 wei)) throw;
+ if(!d.call('function_name', 'arg1', 'arg2')) throw;
+
+
+ uint a = now;
+ uint c = block.timestamp;
+ if(block.timestamp < 100){}
+ chairperson = msg.sender;
+ voters[chairperson].weight = 1;
+ proposals.length = _numProposals;
+ if(!d.send(1 wei)) throw;
+ feed.info.value(10).gas(800)();
+
+ feed.call1(1);
+
+ this.send1(d);
+ }
+
+
+ /// Give $(voter) the right to vote on this ballot.
+ /// May only be called by $(chairperson).
+ function giveRightToVote(address voter, address b) payable returns (bool){
+ if (msg.sender != chairperson || voters[voter].voted) return;
+ voters[voter].weight = 1;
+ return true;
+ }
+
+ /// Delegate your vote to the voter $(to).
+ function delegate(address to) {
+ Voter sender = voters[msg.sender]; // assigns reference
+ if (sender.voted) return;
+ while (voters[to].delegate != address(0) && voters[to].delegate != msg.sender)
+ to = voters[to].delegate;
+ if (to == msg.sender) return;
+ sender.voted = true;
+ sender.delegate = to;
+ Voter delegate = voters[to];
+ if (delegate.voted)
+ proposals[delegate.vote].voteCount += sender.weight;
+ else
+ delegate.weight += sender.weight;
+ }
+
+ /// Give a single vote to proposal $(proposal).
+ function vote(uint8 proposal) {
+ Voter sender = voters[msg.sender];
+ if (sender.voted || proposal >= proposals.length) return;
+ sender.voted = true;
+ sender.vote = proposal;
+ proposals[proposal].voteCount += sender.weight;
+ }
+
+ function winningProposal() constant returns (uint8 winningProposal) {
+ uint256 winningVoteCount = 0;
+ for (uint8 proposal = 0; proposal < proposals.length; proposal++)
+ if (proposals[proposal].voteCount > winningVoteCount) {
+ winningVoteCount = proposals[proposal].voteCount;
+ winningProposal = proposal;
+ }
+ }
+}
diff --git a/remix-analyzer/test/analysis/test-contracts/ballot_withoutWarnings.sol b/remix-analyzer/test/analysis/test-contracts/ballot_withoutWarnings.sol
new file mode 100644
index 0000000000..e273b3da4f
--- /dev/null
+++ b/remix-analyzer/test/analysis/test-contracts/ballot_withoutWarnings.sol
@@ -0,0 +1,31 @@
+pragma solidity ^0.4.0;
+
+/// @title Voting with delegation.
+contract Ballot {
+
+ struct Proposal
+ {
+ bytes32 name; // short name (up to 32 bytes)
+ uint voteCount; // number of accumulated votes
+ }
+
+ // A dynamically-sized array of `Proposal` structs.
+ Proposal[] public proposals;
+
+ /// @dev Computes the winning proposal taking all
+ /// previous votes into account.
+ function winningProposal() constant
+ returns (uint winningProposal)
+ {
+ winningProposal = 0;
+ }
+
+ // Calls winningProposal() function to get the index
+ // of the winner contained in the proposals array and then
+ // returns the name of the winner
+ function winnerName() constant
+ returns (bytes32 winnerName)
+ {
+ winnerName = proposals[winningProposal()].name;
+ }
+}
diff --git a/remix-analyzer/test/analysis/test-contracts/blockLevelCompare.sol b/remix-analyzer/test/analysis/test-contracts/blockLevelCompare.sol
new file mode 100644
index 0000000000..14c4eb9d7a
--- /dev/null
+++ b/remix-analyzer/test/analysis/test-contracts/blockLevelCompare.sol
@@ -0,0 +1,45 @@
+pragma solidity ^0.4.22;
+contract grr {
+ bool breaker;
+
+ function() public {
+ uint a = 1;
+ string memory sig = "withdraw()";
+ uint b = 3;
+
+ bytes4 selector = bytes4(keccak256(sig));
+
+ abi.encode(a,b);
+
+ abi.encodePacked(a,b);
+
+ a = -b;
+
+ a == b;
+
+ if(a == b) {
+ abi.encodeWithSelector(selector, a, b);
+ abi.encodeWithSignature(sig, a, b);
+ }
+
+ if(b < 4) { a == b; }
+
+ if(b > 4) b == a;
+
+ while(true) a == b;
+
+ for(int i = 0; i < 3; i++) b == a;
+
+ while(false) {
+ int c = 3;
+ uint(c) + a;
+
+ c == 5;
+
+ }
+
+ a + b;
+ breaker = false;
+ }
+
+}
\ No newline at end of file
diff --git a/remix-analyzer/test/analysis/test-contracts/cross_contract.sol b/remix-analyzer/test/analysis/test-contracts/cross_contract.sol
new file mode 100644
index 0000000000..426c8d444d
--- /dev/null
+++ b/remix-analyzer/test/analysis/test-contracts/cross_contract.sol
@@ -0,0 +1,19 @@
+pragma solidity ^0.4.0;
+
+contract a {
+
+ uint x;
+
+ function foo() {
+ x++;
+ }
+}
+
+contract b {
+ a x;
+ function bar() constant {
+ address a;
+ a.send(100 wei);
+ x.foo();
+ }
+}
diff --git a/remix-analyzer/test/analysis/test-contracts/ctor.sol b/remix-analyzer/test/analysis/test-contracts/ctor.sol
new file mode 100644
index 0000000000..b250c02626
--- /dev/null
+++ b/remix-analyzer/test/analysis/test-contracts/ctor.sol
@@ -0,0 +1,8 @@
+contract c {
+ uint x;
+ uint x_abc;
+ function c(uint _x, uint _abc) {
+ x=_x;
+ x_abc=_abc;
+ }
+}
\ No newline at end of file
diff --git a/remix-analyzer/test/analysis/test-contracts/deleteDynamicArray.sol b/remix-analyzer/test/analysis/test-contracts/deleteDynamicArray.sol
new file mode 100644
index 0000000000..bb179b21f6
--- /dev/null
+++ b/remix-analyzer/test/analysis/test-contracts/deleteDynamicArray.sol
@@ -0,0 +1,37 @@
+pragma solidity ^0.4.22;
+contract arr {
+ uint[] users;
+
+ bytes access_rights_per_user;
+
+ uint user_index;
+
+ address owner;
+
+ string grr = "message";
+
+ uint[100] last_100_users;
+
+ constructor(address owner1) public {
+ owner = owner1;
+ user_index = 0;
+ }
+
+ function addUser(uint id, byte rights) public{
+ users[user_index] = id;
+ last_100_users[user_index % 100] = id;
+ access_rights_per_user[user_index] = rights;
+ user_index++;
+ }
+
+ function resetState() public{
+ require(msg.sender == owner, grr);
+ delete users;
+ delete access_rights_per_user;
+ delete last_100_users;
+ }
+
+ function bla(string bal) public {
+ grr = bal;
+ }
+}
\ No newline at end of file
diff --git a/remix-analyzer/test/analysis/test-contracts/forgottenReturn.sol b/remix-analyzer/test/analysis/test-contracts/forgottenReturn.sol
new file mode 100644
index 0000000000..eb3df75e44
--- /dev/null
+++ b/remix-analyzer/test/analysis/test-contracts/forgottenReturn.sol
@@ -0,0 +1,13 @@
+contract Sheep {
+ string public name;
+ string public dna;
+ bool g = true;
+ function Sheep(string _name, string _dna) {
+ name = _name;
+ dna = _dna;
+ }
+
+ function geneticallyEngineer(string _dna) returns (bool) {
+ dna = _dna;
+ }
+}
\ No newline at end of file
diff --git a/remix-analyzer/test/analysis/test-contracts/functionParameters.sol b/remix-analyzer/test/analysis/test-contracts/functionParameters.sol
new file mode 100644
index 0000000000..f1a9f4a0f0
--- /dev/null
+++ b/remix-analyzer/test/analysis/test-contracts/functionParameters.sol
@@ -0,0 +1,16 @@
+pragma solidity ^0.4.18;
+
+contract B {
+ function plus(uint a, uint b) pure internal returns (uint) {
+ return a + b;
+ }
+
+ function eval(function (uint, uint) pure internal returns (uint) f, uint x, uint y) pure internal returns (uint) {
+ return f(x, y);
+ }
+
+ function calc(uint x, uint y) pure public returns (uint) {
+ return eval(plus, x, y);
+ // return plus(x, y);
+ }
+}
\ No newline at end of file
diff --git a/remix-analyzer/test/analysis/test-contracts/globals.sol b/remix-analyzer/test/analysis/test-contracts/globals.sol
new file mode 100644
index 0000000000..7df37c757f
--- /dev/null
+++ b/remix-analyzer/test/analysis/test-contracts/globals.sol
@@ -0,0 +1,58 @@
+pragma solidity ^0.4.9;
+contract bla {
+ uint brr;
+ function duper() {
+ brr++;
+ }
+}
+
+contract a is bla {
+
+ function blub() {
+ brr++;
+ }
+
+ function r () {
+ address a;
+ bytes32 hash;
+ uint8 v;
+ bytes32 r;
+ bytes32 s;
+
+ block.blockhash(1);
+ block.coinbase;
+ block.difficulty;
+ block.gaslimit;
+ block.number;
+ block.timestamp;
+ msg.data;
+ msg.gas;
+ msg.sender;
+ msg.value;
+ now;
+ tx.gasprice;
+ tx.origin;
+ // assert(1 == 2);
+ // require(1 == 1);
+ keccak256(a);
+ sha3(a);
+ sha256(a);
+ ripemd160(a);
+ ecrecover(hash, v, r, s);
+ addmod(1, 2, 2);
+ mulmod(4,4,12);
+
+ a.balance;
+ blub();
+ a.send(a.balance);
+
+
+
+ super.duper();
+ //a.transfer(a.balance);
+ selfdestruct(a);
+ //revert();
+ assert(a.balance == 0);
+ }
+
+}
\ No newline at end of file
diff --git a/remix-analyzer/test/analysis/test-contracts/inheritance.sol b/remix-analyzer/test/analysis/test-contracts/inheritance.sol
new file mode 100644
index 0000000000..1491e8fa9f
--- /dev/null
+++ b/remix-analyzer/test/analysis/test-contracts/inheritance.sol
@@ -0,0 +1,40 @@
+pragma solidity ^0.4.9;
+
+contract r {
+ function s() constant {}
+}
+
+contract a is r {
+ uint x = 1;
+
+ function getX() constant returns (uint) {
+ return x;
+ }
+}
+
+contract b is a {
+ uint y = 2;
+ uint x = 3;
+
+
+ function getY(uint z, bool r) returns (uint) {
+ return y++;
+ }
+
+ function getY(string storage n) internal constant returns (uint) { return 10; }
+
+}
+
+contract c is b {
+ string x;
+
+ function d() returns (uint a, uint b) {
+ //d();
+ //sha3("bla");
+ msg.sender.call.gas(200000).value(this.balance)(bytes4(sha3("pay()")));
+ //x++;
+ getY(x);
+ a = getX() + getY(1, false);
+ b = getX() + getY(x);
+ }
+}
diff --git a/remix-analyzer/test/analysis/test-contracts/intDivisionTruncate.sol b/remix-analyzer/test/analysis/test-contracts/intDivisionTruncate.sol
new file mode 100644
index 0000000000..a7c30fbfd1
--- /dev/null
+++ b/remix-analyzer/test/analysis/test-contracts/intDivisionTruncate.sol
@@ -0,0 +1,28 @@
+pragma solidity ^0.4.19;
+
+contract CharityCampaign {
+ mapping (address => uint) contributions;
+ int128 feePercentage;
+ uint p2;
+ address processor;
+ address beneficiary;
+
+ function CharityCampaign(address _beneficiary, int128 _feePercentage) public {
+ processor = msg.sender;
+ beneficiary = _beneficiary;
+ feePercentage = _feePercentage;
+ }
+
+ function contribute() payable public returns (uint feeCollected) {
+ uint fee = msg.value * uint256(feePercentage / 100);
+ fee = msg.value * (p2 / 100);
+ contributions[msg.sender] = msg.value - fee;
+ processor.transfer(fee);
+ return fee;
+ }
+
+ function endCampaign() public {
+ require(msg.sender == processor || msg.sender == beneficiary);
+ selfdestruct(beneficiary);
+ }
+}
\ No newline at end of file
diff --git a/remix-analyzer/test/analysis/test-contracts/library.sol b/remix-analyzer/test/analysis/test-contracts/library.sol
new file mode 100644
index 0000000000..3ca1454d49
--- /dev/null
+++ b/remix-analyzer/test/analysis/test-contracts/library.sol
@@ -0,0 +1,54 @@
+pragma solidity ^0.4.0;
+
+library Set {
+ // We define a new struct datatype that will be used to
+ // hold its data in the calling contract.
+ struct Data { mapping(uint => bool) flags; }
+
+ // Note that the first parameter is of type "storage
+ // reference" and thus only its storage address and not
+ // its contents is passed as part of the call. This is a
+ // special feature of library functions. It is idiomatic
+ // to call the first parameter 'self', if the function can
+ // be seen as a method of that object.
+ function insert(Data storage self, uint value)
+ returns (bool)
+ {
+ if (self.flags[value])
+ return false; // already there
+ self.flags[value] = true;
+
+ return true;
+ }
+
+ function remove(Data storage self, uint value)
+ returns (bool)
+ {
+ if (!self.flags[value])
+ return false; // not there
+ self.flags[value] = false;
+ return true;
+ }
+
+ function contains(Data storage self, uint value)
+ returns (bool)
+ {
+ return self.flags[value];
+ }
+}
+
+
+contract C {
+ Set.Data knownValues;
+
+ function register(uint value) {
+ // The library functions can be called without a
+ // specific instance of the library, since the
+ // "instance" will be the current contract.
+ address a;
+ a.send(10 wei);
+ if (!Set.insert(knownValues, value))
+ throw;
+ }
+ // In this contract, we can also directly access knownValues.flags, if we want.
+}
\ No newline at end of file
diff --git a/remix-analyzer/test/analysis/test-contracts/modifier1.sol b/remix-analyzer/test/analysis/test-contracts/modifier1.sol
new file mode 100644
index 0000000000..a755d763b1
--- /dev/null
+++ b/remix-analyzer/test/analysis/test-contracts/modifier1.sol
@@ -0,0 +1,17 @@
+pragma solidity ^0.4.0;
+
+contract test {
+
+ address owner;
+
+ modifier onlyOwner {
+ var a = 0;
+ if (msg.sender != owner)
+ throw;
+ _;
+ }
+
+ function b(address a) onlyOwner returns (bool) {
+
+ }
+}
\ No newline at end of file
diff --git a/remix-analyzer/test/analysis/test-contracts/modifier2.sol b/remix-analyzer/test/analysis/test-contracts/modifier2.sol
new file mode 100644
index 0000000000..44db1617c7
--- /dev/null
+++ b/remix-analyzer/test/analysis/test-contracts/modifier2.sol
@@ -0,0 +1,28 @@
+pragma solidity ^0.4.0;
+
+contract owned {
+
+ uint r=0;
+
+ modifier ntimes(uint n) {
+ for(uint i=0;i uint) shares;
+ /// Withdraw your share.
+ function withdraw() {
+ var share = shares[msg.sender];
+ shares[msg.sender] = 0;
+ if (!msg.sender.send(share))
+ throw;
+ }
+}
diff --git a/remix-analyzer/test/analysis/test-contracts/reentrant.sol b/remix-analyzer/test/analysis/test-contracts/reentrant.sol
new file mode 100644
index 0000000000..896395e1a6
--- /dev/null
+++ b/remix-analyzer/test/analysis/test-contracts/reentrant.sol
@@ -0,0 +1,49 @@
+pragma solidity ^0.4.0;
+
+contract InfoFeed {
+ uint c;
+ function info() constant returns (uint ret) {return c;}
+ function call1(uint a) constant returns (bool) {return true;}
+}
+
+// THIS CONTRACT CONTAINS A BUG - DO NOT USE
+contract Fund {
+ /// Mapping of ether shares of the contract.
+ //mapping(address => uint) shares;
+ /// Withdraw your share.
+
+ uint c = 0;
+ function withdraw() constant {
+ InfoFeed f;
+
+
+ //shares[msg.sender] /= 1;
+
+ f.info();
+
+ //if (msg.sender.send(shares[msg.sender])) throw;
+ // shares[msg.sender] = 0;
+
+
+ b(true, false);
+ //shares[msg.sender]++;
+ //c++;
+
+ }
+ mapping(address => uint) shares;
+
+ function b(bool a, bool b) returns (bool) {
+ mapping(address => uint) c = shares;
+ c[msg.sender] = 0;
+ //f();
+ //withdraw();
+ //shares[msg.sender]++;
+ //c++;
+ return true;
+ }
+
+ function f() {
+ c++;
+ withdraw();
+ }
+}
diff --git a/remix-analyzer/test/analysis/test-contracts/selfdestruct.sol b/remix-analyzer/test/analysis/test-contracts/selfdestruct.sol
new file mode 100644
index 0000000000..d2e31dfdb0
--- /dev/null
+++ b/remix-analyzer/test/analysis/test-contracts/selfdestruct.sol
@@ -0,0 +1,12 @@
+contract sd {
+
+ function() public payable { }
+
+ function c () public constant {
+ selfdestruct(address(0xdeadbeef));
+ }
+
+ function b () public payable {
+ selfdestruct(address(0xdeadbeef));
+ }
+}
\ No newline at end of file
diff --git a/remix-analyzer/test/analysis/test-contracts/structReentrant.sol b/remix-analyzer/test/analysis/test-contracts/structReentrant.sol
new file mode 100644
index 0000000000..95952ff1ca
--- /dev/null
+++ b/remix-analyzer/test/analysis/test-contracts/structReentrant.sol
@@ -0,0 +1,36 @@
+pragma solidity ^0.4.9;
+
+contract Ballot {
+
+ struct Voter {
+ uint weight;
+ bool voted;
+ uint8 vote;
+ address delegate;
+ baz foo;
+ }
+
+ struct baz{
+ uint bar;
+ }
+
+ mapping(address => Voter) voters;
+
+ /// Create a new ballot with $(_numProposals) different proposals.
+ function bla(address a) {
+ Voter x = voters[a];
+
+ if (!a.send(10))
+ throw;
+
+ //voters[a] = Voter(10,true,1,a);
+ //x.foo.bar *= 100;
+ bli(x);
+ }
+
+ //function bla(){}
+
+ function bli(Voter storage x) private {
+ x.foo.bar++;
+ }
+}
diff --git a/remix-analyzer/test/analysis/test-contracts/thisLocal.sol b/remix-analyzer/test/analysis/test-contracts/thisLocal.sol
new file mode 100644
index 0000000000..e31cf0dbaf
--- /dev/null
+++ b/remix-analyzer/test/analysis/test-contracts/thisLocal.sol
@@ -0,0 +1,16 @@
+pragma solidity ^0.4.0;
+
+contract test {
+
+ function (){
+ address x;
+ this.b(x);
+ x.call('something');
+ x.send(1 wei);
+
+ }
+
+ function b(address a) returns (bool) {
+
+ }
+}
diff --git a/remix-analyzer/test/analysis/test-contracts/transfer.sol b/remix-analyzer/test/analysis/test-contracts/transfer.sol
new file mode 100644
index 0000000000..49ddd515bf
--- /dev/null
+++ b/remix-analyzer/test/analysis/test-contracts/transfer.sol
@@ -0,0 +1,7 @@
+contract c {
+ uint x;
+ function f(address r) {
+ r.transfer(1);
+ x = 2;
+ }
+}
\ No newline at end of file
diff --git a/remix-analyzer/test/tests.js b/remix-analyzer/test/tests.js
new file mode 100644
index 0000000000..0df3206d12
--- /dev/null
+++ b/remix-analyzer/test/tests.js
@@ -0,0 +1,4 @@
+
+require('./analysis/staticAnalysisCommon-test.js')
+require('./analysis/staticAnalysisIntegration-test.js')
+require('./analysis/staticAnalysisIssues-test.js')
diff --git a/remix-debug/README.md b/remix-debug/README.md
new file mode 100644
index 0000000000..f198c26d75
--- /dev/null
+++ b/remix-debug/README.md
@@ -0,0 +1,232 @@
+# `remix-debug`
+
+remix-debug wrap other remix-* libraries and can be used to debug Ethereum transactions.
+
++ [Installation](#installation)
++ [Development](#development)
+
+## Installation
+
+
+```bash
+npm install remix-debug
+```
+
+## Development
+
+```javascript
+var Debugger = require('remix-debug').EthDebugger
+var BreakpointManager = require('remix-debug').EthDebugger
+
+var debugger = new Debugger({
+ compilationResult: () => {
+ return compilationResult // that helps resolving source location
+ }
+})
+
+debugger.addProvider(web3, 'web3')
+debugger.switchProvider('web3')
+
+var breakPointManager = new remixCore.code.BreakpointManager(this.debugger, (sourceLocation) => {
+ // return offsetToLineColumn
+})
+debugger.setBreakpointManager(breakPointManager)
+breakPointManager.add({fileName, row})
+breakPointManager.add({fileName, row})
+
+debugger.debug()
+
+// this.traceManager.getCurrentCalledAddressAt
+
+debugger.event.register('newTraceLoaded', () => {
+ // start doing basic stuff like retrieving step details
+ debugger.traceManager.getCallStackAt(34, (error, callstack) => {})
+})
+
+debugger.callTree.register('callTreeReady', () => {
+ // start doing more complex stuff like resolvng local variables
+ breakPointManager.jumpNextBreakpoint(true)
+
+ var storageView = debugger.storageViewAt(38, ,
+ storageView.storageSlot(0, (error, storage) => {})
+ storageView.storageRange(error, storage) => {}) // retrieve 0 => 1000 slots
+
+ debugger.extractStateAt(23, (error, state) => {
+ debugger.decodeStateAt(23, state, (error, decodedState) => {})
+ })
+
+ debugger.sourceLocationFromVMTraceIndex(, 23, (error, location) => {
+ debugger.decodeLocalsAt(23, location, (error, decodedlocals) => {})
+ })
+
+ debugger.extractLocalsAt(23, (null, locals) => {})
+
+})
+```
+
+## Library
+
+Provides:
+
+```javascript
+{
+ code: {
+ CodeManager: CodeManager,
+ BreakpointManager: BreakpointManager
+ },
+ storage: {
+ StorageViewer: StorageViewer,
+ StorageResolver: StorageResolver
+ },
+ trace: {
+ TraceManager: TraceManager
+ }
+}
+```
+
+
+TraceManager is a convenient way to access a VM Trace and resolve some value from it.
+
+`TraceManager()` :
+
+`function resolveTrace(stepIndex, tx)`
+
+`function init(stepIndex, tx)`
+
+`function inRange(stepIndex, tx)`
+
+`function isLoaded(stepIndex, tx)`
+
+`function getLength(stepIndex, tx)`
+
+`function accumulateStorageChanges(stepIndex, tx)`
+
+`function getAddresses(stepIndex, tx)`
+
+`function getCallDataAt(stepIndex, tx)`
+
+`function getCallStackAt(stepIndex, tx)`
+
+`function getStackAt(stepIndex, tx)`
+
+`function getLastCallChangeSince(stepIndex, tx)`
+
+`function getCurrentCalledAddressAt(stepIndex, tx)`
+
+`function getContractCreationCode(stepIndex, tx)`
+
+`function getMemoryAt(stepIndex, tx)`
+
+`function getCurrentPC(stepIndex, tx)`
+
+`function getReturnValue(stepIndex, tx)`
+
+`function getCurrentStep(stepIndex, tx)`
+
+`function getMemExpand(stepIndex, tx)`
+
+`function getStepCost(stepIndex, tx)`
+
+`function getRemainingGas(stepIndex, tx)`
+
+`function getStepCost(stepIndex, tx)`
+
+`function isCreationStep(stepIndex, tx)`
+
+`function findStepOverBack(stepIndex, tx)`
+
+`function findStepOverForward(stepIndex, tx)`
+
+`function findStepOverBack(stepIndex, tx)`
+
+`function findNextCall(stepIndex, tx)`
+
+`function findStepOut(stepIndex, tx)`
+
+`function checkRequestedStep(stepIndex, tx)`
+
+`function waterfall(stepIndex, tx)`
+
+
+- - - -
+
+`CodeManager(_traceManager)` :
+
+`function getCode(stepIndex, tx)` :
+Resolve the code of the given @arg stepIndex and trigger appropriate event
+
+`function resolveStep(address, cb)` :
+Retrieve the code located at the given @arg address
+
+`function getFunctionFromStep(stepIndex, sourceMap, ast)` :
+Retrieve the called function for the current vm step
+
+`function getInstructionIndex(address, step, callback)` :
+Retrieve the instruction index of the given @arg step
+
+`function getFunctionFromPC(address, pc, sourceMap, ast)` :
+Retrieve the called function for the given @arg pc and @arg address
+
+- - - -
+
+`BreakpointManager(_ethdebugger, _locationToRowConverter)` :
+
+`function jumpNextBreakpoint(defaultToLimit)` :
+start looking for the next breakpoint
+
+`function jumpPreviousBreakpoint(defaultToLimit)` :
+start looking for the previous breakpoint
+
+`function jump(direction, defaultToLimit)` :
+start looking for the previous or next breakpoint
+
+`function hasBreakpointAtLine((fileIndex, line)` :
+check the given pair fileIndex/line against registered breakpoints
+
+`function hasBreakpoint()` :
+return true if current manager has breakpoint
+
+`function add(sourceLocation)` :
+add a new breakpoint to the manager
+
+`function remove(sourceLocation)` :
+remove a breakpoint from the manager
+
+- - - -
+
+`StorageViewer(_context, _storageResolver, _traceManager)` :
+
+`function storageRange(defaultToLimit)` :
+return the storage for the current context (address and vm trace index)
+
+`function storageSlot(defaultToLimit)` :
+return a slot value for the current context (address and vm trace index)
+
+`function isComplete(direction, defaultToLimit)` :
+return True if the storage at @arg address is complete
+
+`function initialMappingsLocation((fileIndex, line)` :
+return all the possible mappings locations for the current context (cached) do not return state changes during the current transaction
+
+`function mappingsLocation()` :
+return all the possible mappings locations for the current context (cached) and current mapping slot. returns state changes during the current transaction
+
+`function extractMappingsLocationChanges(sourceLocation)` :
+retrieve mapping location changes from the storage changes.
+
+- - - -
+
+`StorageResolver()` :
+
+`function storageRange(tx, stepIndex, address, callback)` :
+return the storage for the current context (address and vm trace index)
+
+`function initialPreimagesMappings(tx, stepIndex, address, callback)` :
+return a slot value for the current context (address and vm trace index)
+
+`function storageSlot(slot, tx, stepIndex, address, callback)` :
+return True if the storage at @arg address is complete
+
+`function isComplete(address)` :
+return all the possible mappings locations for the current context (cached) do not return state changes during the current transaction
+
diff --git a/remix-debug/index.js b/remix-debug/index.js
new file mode 100644
index 0000000000..1f0f85d278
--- /dev/null
+++ b/remix-debug/index.js
@@ -0,0 +1,39 @@
+'use strict'
+var EthDebugger = require('./src/Ethdebugger')
+
+var CodeManager = require('./src/code/codeManager')
+var BreakpointManager = require('./src/code/breakpointManager')
+var StorageViewer = require('./src/storage/storageViewer')
+var StorageResolver = require('./src/storage/storageResolver')
+var TraceManager = require('./src/trace/traceManager')
+
+/*
+ Use of breakPointManager :
+
+ var breakPointManager = new BreakpointManager(this.debugger, (sourceLocation) => {
+ return line/column from offset (sourceLocation)
+ })
+ this.debugger.setBreakpointManager(breakPointManager)
+*/
+module.exports = {
+ EthDebugger: EthDebugger,
+ /**
+ * constructor
+ *
+ * @param {Object} _debugger - type of EthDebugger
+ * @return {Function} _locationToRowConverter - function implemented by editor which return a column/line position for a char source location
+ */
+ BreakpointManager: BreakpointManager,
+ code: {
+ CodeManager: CodeManager,
+ BreakpointManager: BreakpointManager
+ },
+ storage: {
+ StorageViewer: StorageViewer,
+ StorageResolver: StorageResolver
+ },
+ trace: {
+ TraceManager: TraceManager
+ }
+}
+
diff --git a/remix-debug/package.json b/remix-debug/package.json
new file mode 100644
index 0000000000..e275a6e8f2
--- /dev/null
+++ b/remix-debug/package.json
@@ -0,0 +1,116 @@
+{
+ "name": "remix-debug",
+ "version": "0.0.9",
+ "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",
+ "dependencies": {
+ "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",
+ "ethereumjs-util": "^4.5.0",
+ "ethereumjs-vm": "^2.3.3",
+ "notify-error": "^1.2.0",
+ "npm-run-all": "^4.1.2",
+ "fast-async": "^6.1.2",
+ "remix-lib": "^0.2.9",
+ "solc": "https://github.com/ethereum/solc-js"
+ },
+ "devDependencies": {
+ "standard": "^7.0.1",
+ "standard-reporter": "^1.0.5",
+ "tape": "^4.6.0"
+ },
+ "scripts": {
+ "build": "mkdirp build; browserify index.js > build/app.js",
+ "lint": "standard | notify-error",
+ "downloadsolc": "cd node_modules/solc && (test -e soljson.js || wget --no-check-certificate https://solc-bin.ethereum.org/soljson.js) && cd ..",
+ "test": "standard && npm run downloadsolc && tape ./test/tests.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/*"
+ ],
+ "parser": "babel-eslint"
+ },
+ "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",
+ {
+ "runtimePatten": null,
+ "compiler": {
+ "promises": true,
+ "es7": true,
+ "noRuntime": true,
+ "wrapAwait": true
+ }
+ }
+ ],
+ "transform-object-assign"
+ ]
+ ],
+ "presets": [
+ "es2015"
+ ]
+ }
+ ]
+ ]
+ }
+}
diff --git a/remix-debug/src/Ethdebugger.js b/remix-debug/src/Ethdebugger.js
new file mode 100644
index 0000000000..6e2c8997dd
--- /dev/null
+++ b/remix-debug/src/Ethdebugger.js
@@ -0,0 +1,240 @@
+'use strict'
+
+var CodeManager = require('./code/codeManager')
+var StorageViewer = require('./storage/storageViewer')
+var StorageResolver = require('./storage/storageResolver')
+var TraceManager = require('./trace/traceManager')
+
+var SolidityProxy = require('./decoder/solidityProxy')
+var stateDecoder = require('./decoder/stateDecoder')
+var localDecoder = require('./decoder/localDecoder')
+var InternalCallTree = require('./decoder/internalCallTree')
+
+var remixLib = require('remix-lib')
+var traceHelper = remixLib.helpers.trace
+var init = remixLib.init
+var executionContext = remixLib.execution.executionContext
+var EventManager = remixLib.EventManager
+var Web3Providers = remixLib.vm.Web3Providers
+var DummyProvider = remixLib.vm.DummyProvider
+
+/**
+ * Ethdebugger is a wrapper around a few classes that helps debugging a transaction
+ *
+ * - Web3Providers - define which environment (web3) the transaction will be retrieved from
+ * - TraceManager - Load / Analyze the trace and retrieve details of specific test
+ * - CodeManager - Retrieve loaded byte code and help to resolve AST item from vmtrace index
+ * - SolidityProxy - Basically used to extract state variable from AST
+ * - Breakpoint Manager - Used to add / remove / jumpto breakpoint
+ * - InternalCallTree - Used to retrieved local variables
+ * - StorageResolver - Help resolving the storage accross different steps
+ *
+ * @param {Map} opts - { function compilationResult } //
+ */
+function Ethdebugger (opts) {
+ this.opts = opts || {}
+ if (!this.opts.compilationResult) this.opts.compilationResult = () => { return null }
+
+ this.web3 = opts.web3
+
+ this.event = new EventManager()
+
+ this.tx
+
+ this.web3Providers = new Web3Providers()
+ this.addProvider('DUMMYWEB3', new DummyProvider())
+ this.switchProvider('DUMMYWEB3')
+
+ this.traceManager = new TraceManager({web3: this.web3})
+ this.codeManager = new CodeManager(this.traceManager)
+ this.solidityProxy = new SolidityProxy(this.traceManager, this.codeManager)
+ this.storageResolver = null
+
+ this.callTree = new InternalCallTree(this.event, this.traceManager, this.solidityProxy, this.codeManager, { includeLocalVariables: true })
+}
+
+Ethdebugger.prototype.setManagers = function () {
+ this.traceManager = new TraceManager({web3: this.web3})
+ this.codeManager = new CodeManager(this.traceManager)
+ this.solidityProxy = new SolidityProxy(this.traceManager, this.codeManager)
+ this.storageResolver = null
+
+ this.callTree = new InternalCallTree(this.event, this.traceManager, this.solidityProxy, this.codeManager, { includeLocalVariables: true })
+}
+
+Ethdebugger.prototype.resolveStep = function (index) {
+ this.codeManager.resolveStep(index, this.tx)
+}
+
+Ethdebugger.prototype.setCompilationResult = function (compilationResult) {
+ if (compilationResult && compilationResult.sources && compilationResult.contracts) {
+ this.solidityProxy.reset(compilationResult)
+ } else {
+ this.solidityProxy.reset({})
+ }
+}
+
+/* resolve source location */
+Ethdebugger.prototype.sourceLocationFromVMTraceIndex = function (address, stepIndex, callback) {
+ this.callTree.sourceLocationTracker.getSourceLocationFromVMTraceIndex(address, stepIndex, this.solidityProxy.contracts, (error, rawLocation) => {
+ callback(error, rawLocation)
+ })
+}
+
+Ethdebugger.prototype.sourceLocationFromInstructionIndex = function (address, instIndex, callback) {
+ this.debugger.callTree.sourceLocationTracker.getSourceLocationFromInstructionIndex(address, instIndex, this.solidityProxy.contracts, function (error, rawLocation) {
+ callback(error, rawLocation)
+ })
+}
+
+/* breakpoint */
+Ethdebugger.prototype.setBreakpointManager = function (breakpointManager) {
+ this.breakpointManager = breakpointManager
+}
+
+/* decode locals */
+Ethdebugger.prototype.extractLocalsAt = function (step, callback) {
+ callback(null, this.callTree.findScope(step))
+}
+
+Ethdebugger.prototype.decodeLocalsAt = function (step, sourceLocation, callback) {
+ this.traceManager.waterfall([
+ this.traceManager.getStackAt,
+ this.traceManager.getMemoryAt,
+ this.traceManager.getCurrentCalledAddressAt],
+ step,
+ (error, result) => {
+ if (!error) {
+ var stack = result[0].value
+ var memory = result[1].value
+ try {
+ var storageViewer = new StorageViewer({
+ stepIndex: step,
+ tx: this.tx,
+ address: result[2].value
+ }, this.storageResolver, this.traceManager)
+ localDecoder.solidityLocals(step, this.callTree, stack, memory, storageViewer, sourceLocation).then((locals) => {
+ if (!locals.error) {
+ callback(null, locals)
+ } else {
+ callback(locals.error)
+ }
+ })
+ } catch (e) {
+ callback(e.message)
+ }
+ } else {
+ callback(error)
+ }
+ })
+}
+
+/* decode state */
+Ethdebugger.prototype.extractStateAt = function (step, callback) {
+ this.solidityProxy.extractStateVariablesAt(step, function (error, stateVars) {
+ callback(error, stateVars)
+ })
+}
+
+Ethdebugger.prototype.decodeStateAt = function (step, stateVars, callback) {
+ this.traceManager.getCurrentCalledAddressAt(step, (error, address) => {
+ if (error) return callback(error)
+ var storageViewer = new StorageViewer({
+ stepIndex: step,
+ tx: this.tx,
+ address: address
+ }, this.storageResolver, this.traceManager)
+ stateDecoder.decodeState(stateVars, storageViewer).then((result) => {
+ if (!result.error) {
+ callback(null, result)
+ } else {
+ callback(result.error)
+ }
+ })
+ })
+}
+
+Ethdebugger.prototype.storageViewAt = function (step, address) {
+ return new StorageViewer({
+ stepIndex: step,
+ tx: this.tx,
+ address: address
+ }, this.storageResolver, this.traceManager)
+}
+/* set env */
+Ethdebugger.prototype.web3 = function () {
+ return this.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 {
+ self.web3 = obj
+ self.setManagers()
+ // self.traceManager.web3 = self.web3
+ executionContext.detectNetwork((error, network) => {
+ if (error || !network) {
+ self.web3Debug = obj
+ self.web3 = obj
+ } else {
+ var webDebugNode = init.web3DebugNode(network.name)
+ self.web3Debug = !webDebugNode ? obj : webDebugNode
+ self.web3 = !webDebugNode ? obj : webDebugNode
+ }
+ self.setManagers()
+ })
+ self.event.trigger('providerChanged', [type])
+ }
+ })
+}
+
+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.unLoad = function () {
+ this.traceManager.init()
+ this.codeManager.clear()
+ this.stepManager.reset()
+ this.event.trigger('traceUnloaded')
+}
+
+Ethdebugger.prototype.debug = function (tx) {
+ if (this.traceManager.isLoading) {
+ return
+ }
+ if (!tx.to) {
+ tx.to = traceHelper.contractCreationToken('0')
+ }
+ this.setCompilationResult(this.opts.compilationResult())
+ 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.event.trigger('newTraceLoaded', [self.traceManager.trace])
+ if (self.breakpointManager && self.breakpointManager.hasBreakpoint()) {
+ self.breakpointManager.jumpNextBreakpoint(false)
+ }
+ self.storageResolver = new StorageResolver({web3: self.traceManager.web3})
+ } else {
+ self.statusMessage = error ? error.message : 'Trace not loaded'
+ }
+ })
+}
+
+module.exports = Ethdebugger
diff --git a/remix-debug/src/code/breakpointManager.js b/remix-debug/src/code/breakpointManager.js
new file mode 100644
index 0000000000..4479f0ec4e
--- /dev/null
+++ b/remix-debug/src/code/breakpointManager.js
@@ -0,0 +1,186 @@
+'use strict'
+var remixLib = require('remix-lib')
+var EventManager = remixLib.EventManager
+var helper = remixLib.helpers.trace
+
+/**
+ * allow to manage breakpoint
+ *
+ * Trigger events: breakpointHit, breakpointAdded, breakpointRemoved
+ */
+class BreakpointManager {
+ /**
+ * constructor
+ *
+ * @param {Object} _debugger - type of EthDebugger
+ * @return {Function} _locationToRowConverter - function implemented by editor which return a column/line position for a char source location
+ */
+ constructor (_debugger, _locationToRowConverter) {
+ this.event = new EventManager()
+ this.debugger = _debugger
+ this.breakpoints = {}
+ this.locationToRowConverter = _locationToRowConverter
+ this.previousLine
+ }
+
+ /**
+ * start looking for the next breakpoint
+ * @param {Bool} defaultToLimit - if true jump to the end of the trace if no more breakpoint found
+ *
+ */
+ async jumpNextBreakpoint (fromStep, defaultToLimit) {
+ this.jump(fromStep || 0, 1, defaultToLimit)
+ }
+
+ /**
+ * start looking for the previous breakpoint
+ * @param {Bool} defaultToLimit - if true jump to the start of the trace if no more breakpoint found
+ *
+ */
+ async jumpPreviousBreakpoint (fromStep, defaultToLimit) {
+ this.jump(fromStep || 0, -1, defaultToLimit)
+ }
+
+ /**
+ * start looking for the previous or next breakpoint
+ * @param {Int} direction - 1 or -1 direction of the search
+ * @param {Bool} defaultToLimit - if true jump to the limit (end if direction is 1, beginning if direction is -1) of the trace if no more breakpoint found
+ *
+ */
+ async jump (fromStep, direction, defaultToLimit) {
+ if (!this.locationToRowConverter) {
+ console.log('row converter not provided')
+ return
+ }
+
+ function depthChange (step, trace) {
+ return trace[step].depth !== trace[step - 1].depth
+ }
+
+ function hitLine (currentStep, sourceLocation, previousSourceLocation, self) {
+ // isJumpDestInstruction -> returning from a internal function call
+ // depthChange -> returning from an external call
+ // sourceLocation.start <= previousSourceLocation.start && ... -> previous src is contained in the current one
+ if ((helper.isJumpDestInstruction(self.debugger.traceManager.trace[currentStep]) && previousSourceLocation.jump === 'o') ||
+ depthChange(currentStep, self.debugger.traceManager.trace) ||
+ (sourceLocation.start <= previousSourceLocation.start &&
+ sourceLocation.start + sourceLocation.length >= previousSourceLocation.start + previousSourceLocation.length)) {
+ return false
+ } else {
+ if (self.debugger.stepManager) self.debugger.stepManager.jumpTo(currentStep)
+ self.event.trigger('breakpointHit', [sourceLocation, currentStep])
+ return true
+ }
+ }
+
+ var sourceLocation
+ var previousSourceLocation
+ var currentStep = fromStep + direction
+ var lineHadBreakpoint = false
+ while (currentStep > 0 && currentStep < this.debugger.traceManager.trace.length) {
+ try {
+ previousSourceLocation = sourceLocation
+ sourceLocation = await this.debugger.callTree.extractSourceLocation(currentStep)
+ } catch (e) {
+ console.log('cannot jump to breakpoint ' + e)
+ return
+ }
+ var lineColumn = this.locationToRowConverter(sourceLocation)
+ if (this.previousLine !== lineColumn.start.line) {
+ if (direction === -1 && lineHadBreakpoint) { // TODO : improve this when we will build the correct structure before hand
+ lineHadBreakpoint = false
+ if (hitLine(currentStep + 1, previousSourceLocation, sourceLocation, this)) {
+ return
+ }
+ }
+ this.previousLine = lineColumn.start.line
+ if (this.hasBreakpointAtLine(sourceLocation.file, lineColumn.start.line)) {
+ lineHadBreakpoint = true
+ if (direction === 1) {
+ if (hitLine(currentStep, sourceLocation, previousSourceLocation, this)) {
+ return
+ }
+ }
+ }
+ }
+ currentStep += direction
+ }
+ this.event.trigger('NoBreakpointHit', [])
+ if (this.debugger.stepManager && defaultToLimit) {
+ if (direction === -1) {
+ this.debugger.stepManager.jumpTo(0)
+ } else if (direction === 1) {
+ this.debugger.stepManager.jumpTo(this.debugger.traceManager.trace.length - 1)
+ }
+ }
+ }
+
+ /**
+ * check the given pair fileIndex/line against registered breakpoints
+ *
+ * @param {Int} fileIndex - index of the file content (from the compilation result)
+ * @param {Int} line - line number where looking for breakpoint
+ * @return {Bool} return true if the given @arg fileIndex @arg line refers to a breakpoint
+ */
+ hasBreakpointAtLine (fileIndex, line) {
+ var filename = this.debugger.solidityProxy.fileNameFromIndex(fileIndex)
+ if (filename && this.breakpoints[filename]) {
+ var sources = this.breakpoints[filename]
+ for (var k in sources) {
+ var source = sources[k]
+ if (line === source.row) {
+ return true
+ }
+ }
+ }
+ return false
+ }
+
+ /**
+ * return true if current manager has breakpoint
+ *
+ * @return {Bool} true if breapoint registered
+ */
+ hasBreakpoint () {
+ for (var k in this.breakpoints) {
+ if (this.breakpoints[k].length) {
+ return true
+ }
+ }
+ return false
+ }
+
+ /**
+ * add a new breakpoint to the manager
+ *
+ * @param {Object} sourceLocation - position of the breakpoint { file: '', row: '', row: ' raw.length) {
+ for (var j = opcode.pushData.length; j < length; j++) {
+ opcode.pushData.push(0)
+ }
+ }
+ i += length
+ }
+ code.push(opcode)
+ }
+ return code
+ },
+
pad: function (num, size) {
var s = num + ''
while (s.length < size) s = '0' + s
diff --git a/remix-debug/src/code/disassembler.js b/remix-debug/src/code/disassembler.js
new file mode 100644
index 0000000000..69c6082c65
--- /dev/null
+++ b/remix-debug/src/code/disassembler.js
@@ -0,0 +1,58 @@
+'use strict'
+
+var parseCode = require('./codeUtils').parseCode
+var remixLib = require('remix-lib')
+var util = remixLib.util
+
+var createExpressions = function (instructions) {
+ var expressions = []
+ var labels = 0
+ for (var i = 0; i < instructions.length; i++) {
+ var expr = instructions[i]
+ expr.functional = false
+ if (expr.name === 'JUMPDEST') {
+ expr.label = 'label' + (++labels)
+ } else if (expr.name.slice(0, 3) === 'DUP') {
+ } else if (expr.name.slice(0, 4) === 'SWAP') {
+ } else if (expr.out <= 1 && expr.in <= expressions.length) {
+ var error = false
+ for (var j = 0; j < expr.in && !error; j++) {
+ var arg = expressions[expressions.length - j - 1]
+ if (!arg.functional || arg.out !== 1) {
+ error = true
+ break
+ }
+ }
+ if (!error) {
+ expr.args = expressions.splice(expressions.length - expr.in)
+ expr.functional = true
+ }
+ }
+ expressions.push(expr)
+ }
+ return expressions
+}
+
+var toString = function (expr) {
+ if (expr.name.slice(0, 4) === 'PUSH') {
+ return util.hexConvert(expr.pushData)
+ } else if (expr.name === 'JUMPDEST') {
+ return expr.label + ':'
+ } else if (expr.args) {
+ return expr.name.toLowerCase() + '(' + expr.args.reverse().map(toString).join(', ') + ')'
+ } else {
+ return expr.name.toLowerCase()
+ }
+}
+
+var disassemble = function (input) {
+ var code = parseCode(util.hexToIntArray(input))
+ return createExpressions(code).map(toString).join('\n')
+}
+
+module.exports = {
+ /**
+ * Disassembler that turns bytecode (as a hex string) into Solidity inline assembly.
+ */
+ disassemble: disassemble
+}
diff --git a/remix-debug/src/code/opcodes.js b/remix-debug/src/code/opcodes.js
new file mode 100644
index 0000000000..765149dbbf
--- /dev/null
+++ b/remix-debug/src/code/opcodes.js
@@ -0,0 +1,184 @@
+'use strict'
+var codes = {
+ // 0x0 range - arithmetic ops
+ // name, baseCost, off stack, on stack, dynamic, async
+ 0x00: ['STOP', 0, 0, 0, false],
+ 0x01: ['ADD', 3, 2, 1, false],
+ 0x02: ['MUL', 5, 2, 1, false],
+ 0x03: ['SUB', 3, 2, 1, false],
+ 0x04: ['DIV', 5, 2, 1, false],
+ 0x05: ['SDIV', 5, 2, 1, false],
+ 0x06: ['MOD', 5, 2, 1, false],
+ 0x07: ['SMOD', 5, 2, 1, false],
+ 0x08: ['ADDMOD', 8, 3, 1, false],
+ 0x09: ['MULMOD', 8, 3, 1, false],
+ 0x0a: ['EXP', 10, 2, 1, false],
+ 0x0b: ['SIGNEXTEND', 5, 2, 1, false],
+
+ // 0x10 range - bit ops
+ 0x10: ['LT', 3, 2, 1, false],
+ 0x11: ['GT', 3, 2, 1, false],
+ 0x12: ['SLT', 3, 2, 1, false],
+ 0x13: ['SGT', 3, 2, 1, false],
+ 0x14: ['EQ', 3, 2, 1, false],
+ 0x15: ['ISZERO', 3, 1, 1, false],
+ 0x16: ['AND', 3, 2, 1, false],
+ 0x17: ['OR', 3, 2, 1, false],
+ 0x18: ['XOR', 3, 2, 1, false],
+ 0x19: ['NOT', 3, 1, 1, false],
+ 0x1a: ['BYTE', 3, 2, 1, false],
+
+ // 0x20 range - crypto
+ 0x20: ['SHA3', 30, 2, 1, false],
+
+ // 0x30 range - closure state
+ 0x30: ['ADDRESS', 2, 0, 1, true],
+ 0x31: ['BALANCE', 400, 1, 1, true, true],
+ 0x32: ['ORIGIN', 2, 0, 1, true],
+ 0x33: ['CALLER', 2, 0, 1, true],
+ 0x34: ['CALLVALUE', 2, 0, 1, true],
+ 0x35: ['CALLDATALOAD', 3, 1, 1, true],
+ 0x36: ['CALLDATASIZE', 2, 0, 1, true],
+ 0x37: ['CALLDATACOPY', 3, 3, 0, true],
+ 0x38: ['CODESIZE', 2, 0, 1, false],
+ 0x39: ['CODECOPY', 3, 3, 0, false],
+ 0x3a: ['GASPRICE', 2, 0, 1, false],
+ 0x3b: ['EXTCODESIZE', 700, 1, 1, true, true],
+ 0x3c: ['EXTCODECOPY', 700, 4, 0, true, true],
+ 0x3d: ['RETURNDATASIZE', 2, 0, 1, true],
+ 0x3e: ['RETURNDATACOPY', 3, 3, 0, true],
+
+ // '0x40' range - block operations
+ 0x40: ['BLOCKHASH', 20, 1, 1, true, true],
+ 0x41: ['COINBASE', 2, 0, 1, true],
+ 0x42: ['TIMESTAMP', 2, 0, 1, true],
+ 0x43: ['NUMBER', 2, 0, 1, true],
+ 0x44: ['DIFFICULTY', 2, 0, 1, true],
+ 0x45: ['GASLIMIT', 2, 0, 1, true],
+
+ // 0x50 range - 'storage' and execution
+ 0x50: ['POP', 2, 1, 0, false],
+ 0x51: ['MLOAD', 3, 1, 1, false],
+ 0x52: ['MSTORE', 3, 2, 0, false],
+ 0x53: ['MSTORE8', 3, 2, 0, false],
+ 0x54: ['SLOAD', 200, 1, 1, true, true],
+ 0x55: ['SSTORE', 0, 2, 0, true, true],
+ 0x56: ['JUMP', 8, 1, 0, false],
+ 0x57: ['JUMPI', 10, 2, 0, false],
+ 0x58: ['PC', 2, 0, 1, false],
+ 0x59: ['MSIZE', 2, 0, 1, false],
+ 0x5a: ['GAS', 2, 0, 1, false],
+ 0x5b: ['JUMPDEST', 1, 0, 0, false],
+
+ // 0x60, range
+ 0x60: ['PUSH1', 3, 0, 1, false],
+ 0x61: ['PUSH2', 3, 0, 1, false],
+ 0x62: ['PUSH3', 3, 0, 1, false],
+ 0x63: ['PUSH4', 3, 0, 1, false],
+ 0x64: ['PUSH5', 3, 0, 1, false],
+ 0x65: ['PUSH6', 3, 0, 1, false],
+ 0x66: ['PUSH7', 3, 0, 1, false],
+ 0x67: ['PUSH8', 3, 0, 1, false],
+ 0x68: ['PUSH9', 3, 0, 1, false],
+ 0x69: ['PUSH10', 3, 0, 1, false],
+ 0x6a: ['PUSH11', 3, 0, 1, false],
+ 0x6b: ['PUSH12', 3, 0, 1, false],
+ 0x6c: ['PUSH13', 3, 0, 1, false],
+ 0x6d: ['PUSH14', 3, 0, 1, false],
+ 0x6e: ['PUSH15', 3, 0, 1, false],
+ 0x6f: ['PUSH16', 3, 0, 1, false],
+ 0x70: ['PUSH17', 3, 0, 1, false],
+ 0x71: ['PUSH18', 3, 0, 1, false],
+ 0x72: ['PUSH19', 3, 0, 1, false],
+ 0x73: ['PUSH20', 3, 0, 1, false],
+ 0x74: ['PUSH21', 3, 0, 1, false],
+ 0x75: ['PUSH22', 3, 0, 1, false],
+ 0x76: ['PUSH23', 3, 0, 1, false],
+ 0x77: ['PUSH24', 3, 0, 1, false],
+ 0x78: ['PUSH25', 3, 0, 1, false],
+ 0x79: ['PUSH26', 3, 0, 1, false],
+ 0x7a: ['PUSH27', 3, 0, 1, false],
+ 0x7b: ['PUSH28', 3, 0, 1, false],
+ 0x7c: ['PUSH29', 3, 0, 1, false],
+ 0x7d: ['PUSH30', 3, 0, 1, false],
+ 0x7e: ['PUSH31', 3, 0, 1, false],
+ 0x7f: ['PUSH32', 3, 0, 1, false],
+
+ 0x80: ['DUP1', 3, 0, 1, false],
+ 0x81: ['DUP2', 3, 0, 1, false],
+ 0x82: ['DUP3', 3, 0, 1, false],
+ 0x83: ['DUP4', 3, 0, 1, false],
+ 0x84: ['DUP5', 3, 0, 1, false],
+ 0x85: ['DUP6', 3, 0, 1, false],
+ 0x86: ['DUP7', 3, 0, 1, false],
+ 0x87: ['DUP8', 3, 0, 1, false],
+ 0x88: ['DUP9', 3, 0, 1, false],
+ 0x89: ['DUP10', 3, 0, 1, false],
+ 0x8a: ['DUP11', 3, 0, 1, false],
+ 0x8b: ['DUP12', 3, 0, 1, false],
+ 0x8c: ['DUP13', 3, 0, 1, false],
+ 0x8d: ['DUP14', 3, 0, 1, false],
+ 0x8e: ['DUP15', 3, 0, 1, false],
+ 0x8f: ['DUP16', 3, 0, 1, false],
+
+ 0x90: ['SWAP1', 3, 0, 0, false],
+ 0x91: ['SWAP2', 3, 0, 0, false],
+ 0x92: ['SWAP3', 3, 0, 0, false],
+ 0x93: ['SWAP4', 3, 0, 0, false],
+ 0x94: ['SWAP5', 3, 0, 0, false],
+ 0x95: ['SWAP6', 3, 0, 0, false],
+ 0x96: ['SWAP7', 3, 0, 0, false],
+ 0x97: ['SWAP8', 3, 0, 0, false],
+ 0x98: ['SWAP9', 3, 0, 0, false],
+ 0x99: ['SWAP10', 3, 0, 0, false],
+ 0x9a: ['SWAP11', 3, 0, 0, false],
+ 0x9b: ['SWAP12', 3, 0, 0, false],
+ 0x9c: ['SWAP13', 3, 0, 0, false],
+ 0x9d: ['SWAP14', 3, 0, 0, false],
+ 0x9e: ['SWAP15', 3, 0, 0, false],
+ 0x9f: ['SWAP16', 3, 0, 0, false],
+
+ 0xa0: ['LOG0', 375, 2, 0, false],
+ 0xa1: ['LOG1', 375, 3, 0, false],
+ 0xa2: ['LOG2', 375, 4, 0, false],
+ 0xa3: ['LOG3', 375, 5, 0, false],
+ 0xa4: ['LOG4', 375, 6, 0, false],
+
+ // '0xf0' range - closures
+ 0xf0: ['CREATE', 32000, 3, 1, true, true],
+ 0xf1: ['CALL', 700, 7, 1, true, true],
+ 0xf2: ['CALLCODE', 700, 7, 1, true, true],
+ 0xf3: ['RETURN', 0, 2, 0, false],
+ 0xf4: ['DELEGATECALL', 700, 6, 1, true, true],
+ 0xfa: ['STATICCALL', 700, 6, 1, true, true],
+ 0xfd: ['REVERT', 0, 2, 0, false],
+
+ // '0x70', range - other
+ 0xfe: ['INVALID', 0, 0, 0, false],
+ 0xff: ['SELFDESTRUCT', 5000, 1, 0, false, true]
+}
+
+module.exports = function (op, full) {
+ var code = codes[op] ? codes[op] : ['INVALID', 0, 0, 0, false, false]
+ var opcode = code[0]
+
+ if (full) {
+ if (opcode === 'LOG') {
+ opcode += op - 0xa0
+ }
+
+ if (opcode === 'PUSH') {
+ opcode += op - 0x5f
+ }
+
+ if (opcode === 'DUP') {
+ opcode += op - 0x7f
+ }
+
+ if (opcode === 'SWAP') {
+ opcode += op - 0x8f
+ }
+ }
+
+ return {name: opcode, fee: code[1], in: code[2], out: code[3], dynamic: code[4], async: code[5]}
+}
diff --git a/remix-debug/src/decoder/astHelper.js b/remix-debug/src/decoder/astHelper.js
new file mode 100644
index 0000000000..88b3a63165
--- /dev/null
+++ b/remix-debug/src/decoder/astHelper.js
@@ -0,0 +1,105 @@
+'use strict'
+var remixLib = require('remix-lib')
+var AstWalker = remixLib.AstWalker
+
+/**
+ * return all contract definitions of the given @astList
+ *
+ * @param {Object} sourcesList - sources list (containing root AST node)
+ * @return {Object} - returns a mapping from AST node ids to AST nodes for the contracts
+ */
+function extractContractDefinitions (sourcesList) {
+ var ret = {
+ contractsById: {},
+ contractsByName: {},
+ sourcesByContract: {}
+ }
+ var walker = new AstWalker()
+ for (var k in sourcesList) {
+ walker.walk(sourcesList[k].legacyAST, { 'ContractDefinition': function (node) {
+ ret.contractsById[node.id] = node
+ ret.sourcesByContract[node.id] = k
+ ret.contractsByName[k + ':' + node.attributes.name] = node
+ return false
+ }})
+ }
+ return ret
+}
+
+/**
+ * returns the linearized base contracts of the contract @arg id
+ *
+ * @param {Int} id - contract id to resolve
+ * @param {Map} contracts - all contracts defined in the current context
+ * @return {Array} - array of base contracts in derived to base order as AST nodes.
+ */
+function getLinearizedBaseContracts (id, contractsById) {
+ return contractsById[id].attributes.linearizedBaseContracts.map(function (id) { return contractsById[id] })
+}
+
+/**
+ * return state var and type definition of the given contract
+ *
+ * @param {String} contractName - contract for which state var should be resolved
+ * @param {Object} sourcesList - sources list (containing root AST node)
+ * @param {Object} [contracts] - map of contract definitions (contains contractsById, contractsByName)
+ * @return {Object} - return an object containing: stateItems - list of all the children node of the @arg contractName
+ * stateVariables - list of all the variable declaration of the @arg contractName
+ */
+function extractStateDefinitions (contractName, sourcesList, contracts) {
+ if (!contracts) {
+ contracts = extractContractDefinitions(sourcesList)
+ }
+ var node = contracts.contractsByName[contractName]
+ if (node) {
+ var stateItems = []
+ var stateVar = []
+ var baseContracts = getLinearizedBaseContracts(node.id, contracts.contractsById)
+ baseContracts.reverse()
+ for (var k in baseContracts) {
+ var ctr = baseContracts[k]
+ for (var i in ctr.children) {
+ var item = ctr.children[i]
+ stateItems.push(item)
+ if (item.name === 'VariableDeclaration') {
+ stateVar.push(item)
+ }
+ }
+ }
+ return {
+ stateDefinitions: stateItems,
+ stateVariables: stateVar
+ }
+ }
+ return null
+}
+
+/**
+ * return state var and type definition of all the contracts from the given @args sourcesList
+ *
+ * @param {Object} sourcesList - sources list (containing root AST node)
+ * @param {Object} [contracts] - map of contract definitions (contains contractsById, contractsByName)
+ * @return {Object} - returns a mapping between contract name and contract state
+ */
+function extractStatesDefinitions (sourcesList, contracts) {
+ if (!contracts) {
+ contracts = extractContractDefinitions(sourcesList)
+ }
+ var ret = {}
+ for (var contract in contracts.contractsById) {
+ var name = contracts.contractsById[contract].attributes.name
+ var source = contracts.sourcesByContract[contract]
+ var fullName = source + ':' + name
+ var state = extractStateDefinitions(fullName, sourcesList, contracts)
+ ret[fullName] = state
+ ret[name] = state // solc < 0.4.9
+ }
+ return ret
+}
+
+module.exports = {
+ extractStatesDefinitions: extractStatesDefinitions,
+ extractStateDefinitions: extractStateDefinitions,
+ extractContractDefinitions: extractContractDefinitions,
+ getLinearizedBaseContracts: getLinearizedBaseContracts
+}
diff --git a/remix-debug/src/decoder/decodeInfo.js b/remix-debug/src/decoder/decodeInfo.js
new file mode 100644
index 0000000000..e65f46ec28
--- /dev/null
+++ b/remix-debug/src/decoder/decodeInfo.js
@@ -0,0 +1,384 @@
+'use strict'
+
+var AddressType = require('./types/Address')
+var ArrayType = require('./types/ArrayType')
+var BoolType = require('./types/Bool')
+var BytesType = require('./types/DynamicByteArray')
+var BytesXType = require('./types/FixedByteArray')
+var EnumType = require('./types/Enum')
+var StringType = require('./types/StringType')
+var StructType = require('./types/Struct')
+var IntType = require('./types/Int')
+var UintType = require('./types/Uint')
+var MappingType = require('./types/Mapping')
+var util = require('./types/util')
+
+/**
+ * mapping decode the given @arg type
+ *
+ * @param {String} type - type given by the AST
+ * @return {Object} returns decoded info about the current type: { storageBytes, typeName}
+ */
+function mapping (type, stateDefinitions, contractName) {
+ var match = type.match(/mapping\((.*?)=>(.*)\)$/)
+ var keyTypeName = match[1].trim()
+ var valueTypeName = match[2].trim()
+
+ var keyType = parseType(keyTypeName, stateDefinitions, contractName, 'storage')
+ var valueType = parseType(valueTypeName, stateDefinitions, contractName, 'storage')
+
+ var underlyingTypes = {
+ 'keyType': keyType,
+ 'valueType': valueType
+ }
+ return new MappingType(underlyingTypes, 'location', util.removeLocation(type))
+}
+
+/**
+ * Uint decode the given @arg type
+ *
+ * @param {String} type - type given by the AST (e.g uint256, uint32)
+ * @return {Object} returns decoded info about the current type: { storageBytes, typeName}
+ */
+function uint (type) {
+ type === 'uint' ? 'uint256' : type
+ var storageBytes = parseInt(type.replace('uint', '')) / 8
+ return new UintType(storageBytes)
+}
+
+/**
+ * Int decode the given @arg type
+ *
+ * @param {String} type - type given by the AST (e.g int256, int32)
+ * @return {Object} returns decoded info about the current type: { storageBytes, typeName}
+ */
+function int (type) {
+ type === 'int' ? 'int256' : type
+ var storageBytes = parseInt(type.replace('int', '')) / 8
+ return new IntType(storageBytes)
+}
+
+/**
+ * Address decode the given @arg type
+ *
+ * @param {String} type - type given by the AST (e.g address)
+ * @return {Object} returns decoded info about the current type: { storageBytes, typeName}
+ */
+function address (type) {
+ return new AddressType()
+}
+
+/**
+ * Bool decode the given @arg type
+ *
+ * @param {String} type - type given by the AST (e.g bool)
+ * @return {Object} returns decoded info about the current type: { storageBytes, typeName}
+ */
+function bool (type) {
+ return new BoolType()
+}
+
+/**
+ * DynamicByteArray decode the given @arg type
+ *
+ * @param {String} type - type given by the AST (e.g bytes storage ref)
+ * @param {null} stateDefinitions - all state definitions given by the AST (including struct and enum type declaration) for all contracts
+ * @param {null} contractName - contract the @args typeName belongs to
+ * @param {String} location - location of the data (storage ref| storage pointer| memory| calldata)
+ * @return {Object} returns decoded info about the current type: { storageBytes, typeName}
+ */
+function dynamicByteArray (type, stateDefinitions, contractName, location) {
+ if (!location) {
+ location = util.extractLocation(type)
+ }
+ if (location) {
+ return new BytesType(location)
+ } else {
+ return null
+ }
+}
+
+/**
+ * FixedByteArray decode the given @arg type
+ *
+ * @param {String} type - type given by the AST (e.g bytes16)
+ * @return {Object} returns decoded info about the current type: { storageBytes, typeName}
+ */
+function fixedByteArray (type) {
+ var storageBytes = parseInt(type.replace('bytes', ''))
+ return new BytesXType(storageBytes)
+}
+
+/**
+ * StringType decode the given @arg type
+ *
+ * @param {String} type - type given by the AST (e.g string storage ref)
+ * @param {null} stateDefinitions - all state definitions given by the AST (including struct and enum type declaration) for all contracts
+ * @param {null} contractName - contract the @args typeName belongs to
+ * @param {String} location - location of the data (storage ref| storage pointer| memory| calldata)
+ * @return {Object} returns decoded info about the current type: { storageBytes, typeName}
+ */
+function stringType (type, stateDefinitions, contractName, location) {
+ if (!location) {
+ location = util.extractLocation(type)
+ }
+ if (location) {
+ return new StringType(location)
+ } else {
+ return null
+ }
+}
+
+/**
+ * ArrayType decode the given @arg type
+ *
+ * @param {String} type - type given by the AST (e.g int256[] storage ref, int256[] storage ref[] storage ref)
+ * @param {Object} stateDefinitions - all state definitions given by the AST (including struct and enum type declaration) for all contracts
+ * @param {String} contractName - contract the @args typeName belongs to
+ * @param {String} location - location of the data (storage ref| storage pointer| memory| calldata)
+ * @return {Object} returns decoded info about the current type: { storageBytes, typeName, arraySize, subArray}
+ */
+function array (type, stateDefinitions, contractName, location) {
+ var arraySize
+ var match = type.match(/(.*)\[(.*?)\]( storage ref| storage pointer| memory| calldata)?$/)
+ if (!match) {
+ console.log('unable to parse type ' + type)
+ return null
+ }
+ if (!location) {
+ location = match[3].trim()
+ }
+ arraySize = match[2] === '' ? 'dynamic' : parseInt(match[2])
+ var underlyingType = parseType(match[1], stateDefinitions, contractName, location)
+ if (underlyingType === null) {
+ console.log('unable to parse type ' + type)
+ return null
+ }
+ return new ArrayType(underlyingType, arraySize, location)
+}
+
+/**
+ * Enum decode the given @arg type
+ *
+ * @param {String} type - type given by the AST (e.g enum enumDef)
+ * @param {Object} stateDefinitions - all state definitions given by the AST (including struct and enum type declaration) for all contracts
+ * @param {String} contractName - contract the @args typeName belongs to
+ * @return {Object} returns decoded info about the current type: { storageBytes, typeName, enum}
+ */
+function enumType (type, stateDefinitions, contractName) {
+ var match = type.match(/enum (.*)/)
+ var enumDef = getEnum(match[1], stateDefinitions, contractName)
+ if (enumDef === null) {
+ console.log('unable to retrieve decode info of ' + type)
+ return null
+ }
+ return new EnumType(enumDef)
+}
+
+/**
+ * Struct decode the given @arg type
+ *
+ * @param {String} type - type given by the AST (e.g struct structDef storage ref)
+ * @param {Object} stateDefinitions - all state definitions given by the AST (including struct and enum type declaration) for all contracts
+ * @param {String} contractName - contract the @args typeName belongs to
+ * @param {String} location - location of the data (storage ref| storage pointer| memory| calldata)
+ * @return {Object} returns decoded info about the current type: { storageBytes, typeName, members}
+ */
+function struct (type, stateDefinitions, contractName, location) {
+ var match = type.match(/struct (\S*?)( storage ref| storage pointer| memory| calldata)?$/)
+ if (match) {
+ if (!location) {
+ location = match[2].trim()
+ }
+ var memberDetails = getStructMembers(match[1], stateDefinitions, contractName, location) // type is used to extract the ast struct definition
+ if (!memberDetails) return null
+ return new StructType(memberDetails, location, match[1])
+ } else {
+ return null
+ }
+}
+
+/**
+ * retrieve enum declaration of the given @arg type
+ *
+ * @param {String} type - type given by the AST (e.g enum enumDef)
+ * @param {Object} stateDefinitions - all state declarations given by the AST (including struct and enum type declaration) for all contracts
+ * @param {String} contractName - contract the @args typeName belongs to
+ * @return {Array} - containing all value declaration of the current enum type
+ */
+function getEnum (type, stateDefinitions, contractName) {
+ var split = type.split('.')
+ if (!split.length) {
+ type = contractName + '.' + type
+ } else {
+ contractName = split[0]
+ }
+ var state = stateDefinitions[contractName]
+ if (state) {
+ for (var dec of state.stateDefinitions) {
+ if (dec.attributes && dec.attributes.name && type === contractName + '.' + dec.attributes.name) {
+ return dec
+ }
+ }
+ }
+ return null
+}
+
+/**
+ * retrieve memebers declared in the given @arg tye
+ *
+ * @param {String} typeName - name of the struct type (e.g struct )
+ * @param {Object} stateDefinitions - all state definition given by the AST (including struct and enum type declaration) for all contracts
+ * @param {String} contractName - contract the @args typeName belongs to
+ * @param {String} location - location of the data (storage ref| storage pointer| memory| calldata)
+ * @return {Array} containing all members of the current struct type
+ */
+function getStructMembers (type, stateDefinitions, contractName, location) {
+ var split = type.split('.')
+ if (!split.length) {
+ type = contractName + '.' + type
+ } else {
+ contractName = split[0]
+ }
+ var state = stateDefinitions[contractName]
+ if (state) {
+ for (var dec of state.stateDefinitions) {
+ if (dec.name === 'StructDefinition' && type === contractName + '.' + dec.attributes.name) {
+ var offsets = computeOffsets(dec.children, stateDefinitions, contractName, location)
+ if (!offsets) {
+ return null
+ }
+ return {
+ members: offsets.typesOffsets,
+ storageSlots: offsets.endLocation.slot
+ }
+ }
+ }
+ }
+ return null
+}
+
+/**
+ * parse the full type
+ *
+ * @param {String} fullType - type given by the AST (ex: uint[2] storage ref[2])
+ * @return {String} returns the token type (used to instanciate the right decoder) (uint[2] storage ref[2] will return 'array', uint256 will return uintX)
+ */
+function typeClass (fullType) {
+ fullType = util.removeLocation(fullType)
+ if (fullType.lastIndexOf(']') === fullType.length - 1) {
+ return 'array'
+ }
+ if (fullType.indexOf('mapping') === 0) {
+ return 'mapping'
+ }
+ if (fullType.indexOf(' ') !== -1) {
+ fullType = fullType.split(' ')[0]
+ }
+ var char = fullType.indexOf('bytes') === 0 ? 'X' : ''
+ return fullType.replace(/[0-9]+/g, char)
+}
+
+/**
+ * parse the type and return an object representing the type
+ *
+ * @param {Object} type - type name given by the ast node
+ * @param {Object} stateDefinitions - all state stateDefinitions given by the AST (including struct and enum type declaration) for all contracts
+ * @param {String} contractName - contract the @args typeName belongs to
+ * @param {String} location - location of the data (storage ref| storage pointer| memory| calldata)
+ * @return {Object} - return the corresponding decoder or null on error
+ */
+function parseType (type, stateDefinitions, contractName, location) {
+ var decodeInfos = {
+ 'contract': address,
+ 'address': address,
+ 'array': array,
+ 'bool': bool,
+ 'bytes': dynamicByteArray,
+ 'bytesX': fixedByteArray,
+ 'enum': enumType,
+ 'string': stringType,
+ 'struct': struct,
+ 'int': int,
+ 'uint': uint,
+ 'mapping': mapping
+ }
+ var currentType = typeClass(type)
+ if (currentType === null) {
+ console.log('unable to retrieve decode info of ' + type)
+ return null
+ }
+ if (decodeInfos[currentType]) {
+ return decodeInfos[currentType](type, stateDefinitions, contractName, location)
+ } else {
+ return null
+ }
+}
+
+/**
+ * compute offset (slot offset and byte offset of the @arg list of types)
+ *
+ * @param {Array} types - list of types
+ * @param {Object} stateDefinitions - all state definitions given by the AST (including struct and enum type declaration) for all contracts
+ * @param {String} contractName - contract the @args typeName belongs to
+ * @param {String} location - location of the data (storage ref| storage pointer| memory| calldata)
+ * @return {Array} - return an array of types item: {name, type, location}. location defines the byte offset and slot offset
+ */
+function computeOffsets (types, stateDefinitions, contractName, location) {
+ var ret = []
+ var storagelocation = {
+ offset: 0,
+ slot: 0
+ }
+ for (var i in types) {
+ var variable = types[i]
+ var type = parseType(variable.attributes.type, stateDefinitions, contractName, location)
+ if (!type) {
+ console.log('unable to retrieve decode info of ' + variable.attributes.type)
+ return null
+ }
+ if (!variable.attributes.constant && storagelocation.offset + type.storageBytes > 32) {
+ storagelocation.slot++
+ storagelocation.offset = 0
+ }
+ ret.push({
+ name: variable.attributes.name,
+ type: type,
+ constant: variable.attributes.constant,
+ storagelocation: {
+ offset: variable.attributes.constant ? 0 : storagelocation.offset,
+ slot: variable.attributes.constant ? 0 : storagelocation.slot
+ }
+ })
+ if (!variable.attributes.constant) {
+ if (type.storageSlots === 1 && storagelocation.offset + type.storageBytes <= 32) {
+ storagelocation.offset += type.storageBytes
+ } else {
+ storagelocation.slot += type.storageSlots
+ storagelocation.offset = 0
+ }
+ }
+ }
+ if (storagelocation.offset > 0) {
+ storagelocation.slot++
+ }
+ return {
+ typesOffsets: ret,
+ endLocation: storagelocation
+ }
+}
+
+module.exports = {
+ parseType: parseType,
+ computeOffsets: computeOffsets,
+ Uint: uint,
+ Address: address,
+ Bool: bool,
+ DynamicByteArray: dynamicByteArray,
+ FixedByteArray: fixedByteArray,
+ Int: int,
+ String: stringType,
+ Array: array,
+ Enum: enumType,
+ Struct: struct
+}
diff --git a/remix-debug/src/decoder/internalCallTree.js b/remix-debug/src/decoder/internalCallTree.js
new file mode 100644
index 0000000000..1f2559c0d8
--- /dev/null
+++ b/remix-debug/src/decoder/internalCallTree.js
@@ -0,0 +1,293 @@
+'use strict'
+var remixLib = require('remix-lib')
+var SourceLocationTracker = remixLib.SourceLocationTracker
+var AstWalker = remixLib.AstWalker
+var EventManager = remixLib.EventManager
+var decodeInfo = require('./decodeInfo')
+var util = remixLib.util
+var traceHelper = remixLib.helpers.trace
+var typesUtil = require('./types/util.js')
+
+/**
+ * Tree representing internal jump into function.
+ * Triggers `callTreeReady` event when tree is ready
+ * Triggers `callTreeBuildFailed` event when tree fails to build
+ */
+class InternalCallTree {
+ /**
+ * constructor
+ *
+ * @param {Object} debuggerEvent - event declared by the debugger (EthDebugger)
+ * @param {Object} traceManager - trace manager
+ * @param {Object} solidityProxy - solidity proxy
+ * @param {Object} codeManager - code manager
+ * @param {Object} opts - { includeLocalVariables }
+ */
+ constructor (debuggerEvent, traceManager, solidityProxy, codeManager, opts) {
+ this.includeLocalVariables = opts.includeLocalVariables
+ this.event = new EventManager()
+ this.solidityProxy = solidityProxy
+ this.traceManager = traceManager
+ this.sourceLocationTracker = new SourceLocationTracker(codeManager)
+ debuggerEvent.register('newTraceLoaded', (trace) => {
+ this.reset()
+ if (!this.solidityProxy.loaded()) {
+ this.event.trigger('callTreeBuildFailed', ['compilation result not loaded. Cannot build internal call tree'])
+ } else {
+ buildTree(this, 0, '', true).then((result) => {
+ if (result.error) {
+ this.event.trigger('callTreeBuildFailed', [result.error])
+ } else {
+ console.log('ready')
+ createReducedTrace(this, traceManager.trace.length - 1)
+ this.event.trigger('callTreeReady', [this.scopes, this.scopeStarts])
+ }
+ }, (reason) => {
+ console.log('analyzing trace falls ' + reason)
+ this.event.trigger('callTreeNotReady', [reason])
+ })
+ }
+ })
+ }
+
+ /**
+ * reset tree
+ *
+ */
+ reset () {
+ /*
+ scopes: map of scopes defined by range in the vmtrace {firstStep, lastStep, locals}. Keys represent the level of deepness (scopeId)
+ */
+ this.scopes = {}
+ /*
+ scopeStart: represent start of a new scope. Keys are index in the vmtrace, values are scopeId
+ */
+ this.sourceLocationTracker.clearCache()
+ this.functionCallStack = []
+ this.scopeStarts = {}
+ this.variableDeclarationByFile = {}
+ this.functionDefinitionByFile = {}
+ this.astWalker = new AstWalker()
+ this.reducedTrace = []
+ }
+
+ /**
+ * find the scope given @arg vmTraceIndex
+ *
+ * @param {Int} vmtraceIndex - index on the vm trace
+ */
+ findScope (vmtraceIndex) {
+ var scopes = Object.keys(this.scopeStarts)
+ if (!scopes.length) {
+ return null
+ }
+ var scopeId = util.findLowerBoundValue(vmtraceIndex, scopes)
+ scopeId = this.scopeStarts[scopeId]
+ var scope = this.scopes[scopeId]
+ while (scope.lastStep && scope.lastStep < vmtraceIndex && scope.firstStep > 0) {
+ var matched = scopeId.match(/(.\d|\d)$/)
+ scopeId = scopeId.replace(matched[1], '')
+ scope = this.scopes[scopeId]
+ }
+ return scope
+ }
+
+ extractSourceLocation (step) {
+ var self = this
+ return new Promise(function (resolve, reject) {
+ self.traceManager.getCurrentCalledAddressAt(step, (error, address) => {
+ if (!error) {
+ self.sourceLocationTracker.getSourceLocationFromVMTraceIndex(address, step, self.solidityProxy.contracts, (error, sourceLocation) => {
+ if (!error) {
+ return resolve(sourceLocation)
+ } else {
+ return reject('InternalCallTree - Cannot retrieve sourcelocation for step ' + step + ' ' + error)
+ }
+ })
+ } else {
+ return reject('InternalCallTree - Cannot retrieve address for step ' + step + ' ' + error)
+ }
+ })
+ })
+ }
+}
+
+async function buildTree (tree, step, scopeId, isExternalCall) {
+ let subScope = 1
+ tree.scopeStarts[step] = scopeId
+ tree.scopes[scopeId] = { firstStep: step, locals: {} }
+
+ function callDepthChange (step, trace) {
+ if (step + 1 < trace.length) {
+ return trace[step].depth !== trace[step + 1].depth
+ }
+ return false
+ }
+
+ function includedSource (source, included) {
+ return (included.start !== -1 &&
+ included.length !== -1 &&
+ included.file !== -1 &&
+ included.start >= source.start &&
+ included.start + included.length <= source.start + source.length &&
+ included.file === source.file)
+ }
+
+ var currentSourceLocation = {start: -1, length: -1, file: -1}
+ var previousSourceLocation = currentSourceLocation
+ while (step < tree.traceManager.trace.length) {
+ var sourceLocation
+ var newLocation = false
+ try {
+ sourceLocation = await tree.extractSourceLocation(step)
+ if (!includedSource(sourceLocation, currentSourceLocation)) {
+ tree.reducedTrace.push(step)
+ currentSourceLocation = sourceLocation
+ newLocation = true
+ }
+ } catch (e) {
+ return { outStep: step, error: 'InternalCallTree - Error resolving source location. ' + step + ' ' + e }
+ }
+ if (!sourceLocation) {
+ return { outStep: step, error: 'InternalCallTree - No source Location. ' + step }
+ }
+ var isCallInstruction = traceHelper.isCallInstruction(tree.traceManager.trace[step])
+ if (isCallInstruction || sourceLocation.jump === 'i') {
+ try {
+ var externalCallResult = await buildTree(tree, step + 1, scopeId === '' ? subScope.toString() : scopeId + '.' + subScope, isCallInstruction)
+ if (externalCallResult.error) {
+ return { outStep: step, error: 'InternalCallTree - ' + externalCallResult.error }
+ } else {
+ step = externalCallResult.outStep
+ subScope++
+ }
+ } catch (e) {
+ return { outStep: step, error: 'InternalCallTree - ' + e.message }
+ }
+ } else if ((isExternalCall && callDepthChange(step, tree.traceManager.trace)) || (!isExternalCall && sourceLocation.jump === 'o')) {
+ tree.scopes[scopeId].lastStep = step
+ return { outStep: step + 1 }
+ } else {
+ if (tree.includeLocalVariables) {
+ includeVariableDeclaration(tree, step, sourceLocation, scopeId, newLocation, previousSourceLocation)
+ }
+ previousSourceLocation = sourceLocation
+ step++
+ }
+ }
+ return { outStep: step }
+}
+
+function createReducedTrace (tree, index) {
+ tree.reducedTrace.push(index)
+}
+
+function includeVariableDeclaration (tree, step, sourceLocation, scopeId, newLocation, previousSourceLocation) {
+ var variableDeclaration = resolveVariableDeclaration(tree, step, sourceLocation)
+ if (variableDeclaration && !tree.scopes[scopeId].locals[variableDeclaration.attributes.name]) {
+ tree.traceManager.getStackAt(step, (error, stack) => {
+ if (!error) {
+ tree.solidityProxy.contractNameAt(step, (error, contractName) => { // cached
+ if (!error) {
+ var states = tree.solidityProxy.extractStatesDefinitions()
+ var location = typesUtil.extractLocationFromAstVariable(variableDeclaration)
+ location = location === 'default' ? 'storage' : location
+ tree.scopes[scopeId].locals[variableDeclaration.attributes.name] = {
+ name: variableDeclaration.attributes.name,
+ type: decodeInfo.parseType(variableDeclaration.attributes.type, states, contractName, location),
+ stackDepth: stack.length,
+ sourceLocation: sourceLocation
+ }
+ }
+ })
+ }
+ })
+ }
+ var functionDefinition = resolveFunctionDefinition(tree, step, previousSourceLocation)
+ if (functionDefinition && (newLocation && traceHelper.isJumpDestInstruction(tree.traceManager.trace[step - 1]) || functionDefinition.attributes.isConstructor)) {
+ tree.functionCallStack.push(step)
+ // means: the previous location was a function definition && JUMPDEST
+ // => we are at the beginning of the function and input/output are setup
+ tree.solidityProxy.contractNameAt(step, (error, contractName) => { // cached
+ if (!error) {
+ tree.traceManager.getStackAt(step, (error, stack) => {
+ if (!error) {
+ var states = tree.solidityProxy.extractStatesDefinitions()
+ // input params
+ addParams(functionDefinition.children[0], tree, scopeId, states, contractName, previousSourceLocation, stack.length, functionDefinition.children[0].children.length, -1)
+ // output params
+ addParams(functionDefinition.children[1], tree, scopeId, states, contractName, previousSourceLocation, stack.length, 0, 1)
+ }
+ })
+ }
+ })
+ }
+}
+
+function resolveVariableDeclaration (tree, step, sourceLocation) {
+ if (!tree.variableDeclarationByFile[sourceLocation.file]) {
+ var ast = tree.solidityProxy.ast(sourceLocation)
+ if (ast) {
+ tree.variableDeclarationByFile[sourceLocation.file] = extractVariableDeclarations(ast, tree.astWalker)
+ } else {
+ console.log('Ast not found for step ' + step + '. file ' + sourceLocation.file)
+ return null
+ }
+ }
+ return tree.variableDeclarationByFile[sourceLocation.file][sourceLocation.start + ':' + sourceLocation.length + ':' + sourceLocation.file]
+}
+
+function resolveFunctionDefinition (tree, step, sourceLocation) {
+ if (!tree.functionDefinitionByFile[sourceLocation.file]) {
+ var ast = tree.solidityProxy.ast(sourceLocation)
+ if (ast) {
+ tree.functionDefinitionByFile[sourceLocation.file] = extractFunctionDefinitions(ast, tree.astWalker)
+ } else {
+ console.log('Ast not found for step ' + step + '. file ' + sourceLocation.file)
+ return null
+ }
+ }
+ return tree.functionDefinitionByFile[sourceLocation.file][sourceLocation.start + ':' + sourceLocation.length + ':' + sourceLocation.file]
+}
+
+function extractVariableDeclarations (ast, astWalker) {
+ var ret = {}
+ astWalker.walk(ast, (node) => {
+ if (node.name === 'VariableDeclaration') {
+ ret[node.src] = node
+ }
+ return true
+ })
+ return ret
+}
+
+function extractFunctionDefinitions (ast, astWalker) {
+ var ret = {}
+ astWalker.walk(ast, (node) => {
+ if (node.name === 'FunctionDefinition') {
+ ret[node.src] = node
+ }
+ return true
+ })
+ return ret
+}
+
+function addParams (parameterList, tree, scopeId, states, contractName, sourceLocation, stackLength, stackPosition, dir) {
+ for (var inputParam in parameterList.children) {
+ var param = parameterList.children[inputParam]
+ var stackDepth = stackLength + (dir * stackPosition)
+ if (stackDepth >= 0) {
+ var location = typesUtil.extractLocationFromAstVariable(param)
+ location = location === 'default' ? 'memory' : location
+ tree.scopes[scopeId].locals[param.attributes.name] = {
+ name: param.attributes.name,
+ type: decodeInfo.parseType(param.attributes.type, states, contractName, location),
+ stackDepth: stackDepth,
+ sourceLocation: sourceLocation
+ }
+ }
+ stackPosition += dir
+ }
+}
+
+module.exports = InternalCallTree
diff --git a/remix-debug/src/decoder/localDecoder.js b/remix-debug/src/decoder/localDecoder.js
new file mode 100644
index 0000000000..650f5bcad3
--- /dev/null
+++ b/remix-debug/src/decoder/localDecoder.js
@@ -0,0 +1,40 @@
+'use strict'
+
+async function solidityLocals (vmtraceIndex, internalTreeCall, stack, memory, storageResolver, currentSourceLocation) {
+ var scope = internalTreeCall.findScope(vmtraceIndex)
+ if (!scope) {
+ var error = { 'message': 'Can\'t display locals. reason: compilation result might not have been provided' }
+ throw error
+ }
+ var locals = {}
+ memory = formatMemory(memory)
+ var anonymousIncr = 1
+ for (var local in scope.locals) {
+ var variable = scope.locals[local]
+ if (variable.stackDepth < stack.length && variable.sourceLocation.start <= currentSourceLocation.start) {
+ var name = variable.name
+ if (name === '') {
+ name = '<' + anonymousIncr + '>'
+ anonymousIncr++
+ }
+ try {
+ locals[name] = await variable.type.decodeFromStack(variable.stackDepth, stack, memory, storageResolver)
+ } catch (e) {
+ console.log(e)
+ locals[name] = ''
+ }
+ }
+ }
+ return locals
+}
+
+function formatMemory (memory) {
+ if (memory instanceof Array) {
+ memory = memory.join('').replace(/0x/g, '')
+ }
+ return memory
+}
+
+module.exports = {
+ solidityLocals: solidityLocals
+}
diff --git a/remix-debug/src/decoder/solidityProxy.js b/remix-debug/src/decoder/solidityProxy.js
new file mode 100644
index 0000000000..63a42670e3
--- /dev/null
+++ b/remix-debug/src/decoder/solidityProxy.js
@@ -0,0 +1,161 @@
+'use strict'
+var remixLib = require('remix-lib')
+var traceHelper = remixLib.helpers.trace
+var stateDecoder = require('./stateDecoder')
+var astHelper = require('./astHelper')
+var util = remixLib.util
+
+class SolidityProxy {
+ constructor (traceManager, codeManager) {
+ this.cache = new Cache()
+ this.reset({})
+ this.traceManager = traceManager
+ this.codeManager = codeManager
+ }
+
+ /**
+ * reset the cache and apply a new @arg compilationResult
+ *
+ * @param {Object} compilationResult - result os a compilatiion (diectly returned by the compiler)
+ */
+ reset (compilationResult) {
+ this.sources = compilationResult.sources
+ this.contracts = compilationResult.contracts
+ this.cache.reset()
+ }
+
+ /**
+ * check if the object has been properly loaded
+ *
+ * @return {Bool} - returns true if a compilation result has been applied
+ */
+ loaded () {
+ return this.contracts !== undefined
+ }
+
+ /**
+ * retrieve the compiled contract name at the @arg vmTraceIndex (cached)
+ *
+ * @param {Int} vmTraceIndex - index in the vm trave where to resolve the executed contract name
+ * @param {Function} cb - callback returns (error, contractName)
+ */
+ contractNameAt (vmTraceIndex, cb) {
+ this.traceManager.getCurrentCalledAddressAt(vmTraceIndex, (error, address) => {
+ if (error) {
+ cb(error)
+ } else {
+ if (this.cache.contractNameByAddress[address]) {
+ cb(null, this.cache.contractNameByAddress[address])
+ } else {
+ this.codeManager.getCode(address, (error, code) => {
+ if (error) {
+ cb(error)
+ } else {
+ var contractName = contractNameFromCode(this.contracts, code.bytecode, address)
+ this.cache.contractNameByAddress[address] = contractName
+ cb(null, contractName)
+ }
+ })
+ }
+ }
+ })
+ }
+
+ /**
+ * extract the state variables of the given compiled @arg contractName (cached)
+ *
+ * @param {String} contractName - name of the contract to retrieve state variables from
+ * @return {Object} - returns state variables of @args contractName
+ */
+ extractStatesDefinitions () {
+ if (!this.cache.contractDeclarations) {
+ this.cache.contractDeclarations = astHelper.extractContractDefinitions(this.sources)
+ }
+ if (!this.cache.statesDefinitions) {
+ this.cache.statesDefinitions = astHelper.extractStatesDefinitions(this.sources, this.cache.contractDeclarations)
+ }
+ return this.cache.statesDefinitions
+ }
+
+ /**
+ * extract the state variables of the given compiled @arg contractName (cached)
+ *
+ * @param {String} contractName - name of the contract to retrieve state variables from
+ * @return {Object} - returns state variables of @args contractName
+ */
+ extractStateVariables (contractName) {
+ if (!this.cache.stateVariablesByContractName[contractName]) {
+ this.cache.stateVariablesByContractName[contractName] = stateDecoder.extractStateVariables(contractName, this.sources)
+ }
+ return this.cache.stateVariablesByContractName[contractName]
+ }
+
+ /**
+ * extract the state variables of the given compiled @arg vmtraceIndex (cached)
+ *
+ * @param {Int} vmTraceIndex - index in the vm trave where to resolve the state variables
+ * @return {Object} - returns state variables of @args vmTraceIndex
+ */
+ extractStateVariablesAt (vmtraceIndex, cb) {
+ this.contractNameAt(vmtraceIndex, (error, contractName) => {
+ if (error) {
+ cb(error)
+ } else {
+ cb(null, this.extractStateVariables(contractName))
+ }
+ })
+ }
+
+ /**
+ * get the AST of the file declare in the @arg sourceLocation
+ *
+ * @param {Object} sourceLocation - source location containing the 'file' to retrieve the AST from
+ * @return {Object} - AST of the current file
+ */
+ ast (sourceLocation) {
+ var file = this.fileNameFromIndex(sourceLocation.file)
+ if (this.sources[file]) {
+ return this.sources[file].legacyAST
+ } else {
+ console.log('AST not found for file id ' + sourceLocation.file)
+ return null
+ }
+ }
+
+ /**
+ * get the filename refering to the index from the compilation result
+ *
+ * @param {Int} index - index of the filename
+ * @return {String} - filename
+ */
+ fileNameFromIndex (index) {
+ return Object.keys(this.contracts)[index]
+ }
+}
+
+function contractNameFromCode (contracts, code, address) {
+ var isCreation = traceHelper.isContractCreation(address)
+ for (var file in contracts) {
+ for (var contract in contracts[file]) {
+ var bytecode = isCreation ? contracts[file][contract].evm.bytecode.object : contracts[file][contract].evm.deployedBytecode.object
+ if (util.compareByteCode(code, '0x' + bytecode)) {
+ return contract
+ }
+ }
+ }
+ return null
+}
+
+class Cache {
+ constructor () {
+ this.reset()
+ }
+ reset () {
+ this.contractNameByAddress = {}
+ this.stateVariablesByContractName = {}
+ this.contractDeclarations = null
+ this.statesDefinitions = null
+ }
+}
+
+module.exports = SolidityProxy
diff --git a/remix-debug/src/decoder/stateDecoder.js b/remix-debug/src/decoder/stateDecoder.js
new file mode 100644
index 0000000000..43baa32225
--- /dev/null
+++ b/remix-debug/src/decoder/stateDecoder.js
@@ -0,0 +1,71 @@
+var astHelper = require('./astHelper')
+var decodeInfo = require('./decodeInfo')
+
+/**
+ * decode the contract state storage
+ *
+ * @param {Array} storage location - location of all state variables
+ * @param {Object} storageResolver - resolve storage queries
+ * @return {Map} - decoded state variable
+ */
+async function decodeState (stateVars, storageResolver) {
+ var ret = {}
+ for (var k in stateVars) {
+ var stateVar = stateVars[k]
+ try {
+ var decoded = await stateVar.type.decodeFromStorage(stateVar.storagelocation, storageResolver)
+ decoded.constant = stateVar.constant
+ if (decoded.constant) {
+ decoded.value = ''
+ }
+ ret[stateVar.name] = decoded
+ } catch (e) {
+ console.log(e)
+ ret[stateVar.name] = ''
+ }
+ }
+ return ret
+}
+
+/**
+ * return all storage location variables of the given @arg contractName
+ *
+ * @param {String} contractName - name of the contract
+ * @param {Object} sourcesList - sources list
+ * @return {Object} - return the location of all contract variables in the storage
+ */
+function extractStateVariables (contractName, sourcesList) {
+ var states = astHelper.extractStatesDefinitions(sourcesList)
+ if (!states[contractName]) {
+ return []
+ }
+ var types = states[contractName].stateVariables
+ var offsets = decodeInfo.computeOffsets(types, states, contractName, 'storage')
+ if (!offsets) {
+ return [] // TODO should maybe return an error
+ }
+ return offsets.typesOffsets
+}
+
+/**
+ * return the state of the given @a contractName as a json object
+ *
+ * @param {Object} storageResolver - resolve storage queries
+ * @param {astList} astList - AST nodes of all the sources
+ * @param {String} contractName - contract for which state var should be resolved
+ * @return {Map} - return the state of the contract
+ */
+async function solidityState (storageResolver, astList, contractName) {
+ var stateVars = extractStateVariables(contractName, astList)
+ try {
+ return await decodeState(stateVars, storageResolver)
+ } catch (e) {
+ return ''
+ }
+}
+
+module.exports = {
+ solidityState: solidityState,
+ extractStateVariables: extractStateVariables,
+ decodeState: decodeState
+}
diff --git a/remix-debug/src/decoder/types/Address.js b/remix-debug/src/decoder/types/Address.js
new file mode 100644
index 0000000000..9369fd2258
--- /dev/null
+++ b/remix-debug/src/decoder/types/Address.js
@@ -0,0 +1,19 @@
+'use strict'
+var util = require('./util')
+var ValueType = require('./ValueType')
+
+class Address extends ValueType {
+ constructor () {
+ super(1, 20, 'address')
+ }
+
+ decodeValue (value) {
+ if (!value) {
+ return '0x0000000000000000000000000000000000000000'
+ } else {
+ return '0x' + util.extractHexByteSlice(value, this.storageBytes, 0).toUpperCase()
+ }
+ }
+}
+
+module.exports = Address
diff --git a/remix-debug/src/decoder/types/ArrayType.js b/remix-debug/src/decoder/types/ArrayType.js
new file mode 100644
index 0000000000..d8edede107
--- /dev/null
+++ b/remix-debug/src/decoder/types/ArrayType.js
@@ -0,0 +1,100 @@
+'use strict'
+var util = require('./util')
+var remixLib = require('remix-lib')
+var sha3256 = remixLib.util.sha3_256
+var BN = require('ethereumjs-util').BN
+var RefType = require('./RefType')
+
+class ArrayType extends RefType {
+
+ constructor (underlyingType, arraySize, location) {
+ var storageSlots = null
+ if (arraySize === 'dynamic') {
+ storageSlots = 1
+ } else {
+ if (underlyingType.storageBytes < 32) {
+ var itemPerSlot = Math.floor(32 / underlyingType.storageBytes)
+ storageSlots = Math.ceil(arraySize / itemPerSlot)
+ } else {
+ storageSlots = arraySize * underlyingType.storageSlots
+ }
+ }
+ var size = arraySize !== 'dynamic' ? arraySize : ''
+ super(storageSlots, 32, underlyingType.typeName + '[' + size + ']', location)
+ this.underlyingType = underlyingType
+ this.arraySize = arraySize
+ }
+
+ async decodeFromStorage (location, storageResolver) {
+ var ret = []
+ var size = null
+ var slotValue
+ try {
+ slotValue = await util.extractHexValue(location, storageResolver, this.storageBytes)
+ } catch (e) {
+ console.log(e)
+ return {
+ value: '',
+ type: this.typeName
+ }
+ }
+ var currentLocation = {
+ offset: 0,
+ slot: location.slot
+ }
+ if (this.arraySize === 'dynamic') {
+ size = util.toBN('0x' + slotValue)
+ currentLocation.slot = sha3256(location.slot)
+ } else {
+ size = new BN(this.arraySize)
+ }
+ var k = util.toBN(0)
+ for (; k.lt(size) && k.ltn(300); k.iaddn(1)) {
+ try {
+ ret.push(await this.underlyingType.decodeFromStorage(currentLocation, storageResolver))
+ } catch (e) {
+ return {
+ value: '',
+ type: this.typeName
+ }
+ }
+ if (this.underlyingType.storageSlots === 1 && location.offset + this.underlyingType.storageBytes <= 32) {
+ currentLocation.offset += this.underlyingType.storageBytes
+ if (currentLocation.offset + this.underlyingType.storageBytes > 32) {
+ currentLocation.offset = 0
+ currentLocation.slot = '0x' + util.add(currentLocation.slot, 1).toString(16)
+ }
+ } else {
+ currentLocation.slot = '0x' + util.add(currentLocation.slot, this.underlyingType.storageSlots).toString(16)
+ currentLocation.offset = 0
+ }
+ }
+ return {
+ value: ret,
+ length: '0x' + size.toString(16),
+ type: this.typeName
+ }
+ }
+
+ decodeFromMemoryInternal (offset, memory) {
+ var ret = []
+ var length = this.arraySize
+ if (this.arraySize === 'dynamic') {
+ length = memory.substr(2 * offset, 64)
+ length = parseInt(length, 16)
+ offset = offset + 32
+ }
+ for (var k = 0; k < length; k++) {
+ var contentOffset = offset
+ ret.push(this.underlyingType.decodeFromMemory(contentOffset, memory))
+ offset += 32
+ }
+ return {
+ value: ret,
+ length: '0x' + length.toString(16),
+ type: this.typeName
+ }
+ }
+}
+
+module.exports = ArrayType
diff --git a/remix-debug/src/decoder/types/Bool.js b/remix-debug/src/decoder/types/Bool.js
new file mode 100644
index 0000000000..32b63d061e
--- /dev/null
+++ b/remix-debug/src/decoder/types/Bool.js
@@ -0,0 +1,20 @@
+'use strict'
+var ValueType = require('./ValueType')
+var util = require('./util')
+
+class Bool extends ValueType {
+ constructor () {
+ super(1, 1, 'bool')
+ }
+
+ decodeValue (value) {
+ if (!value) {
+ return false
+ } else {
+ value = util.extractHexByteSlice(value, this.storageBytes, 0)
+ return value !== '00'
+ }
+ }
+}
+
+module.exports = Bool
diff --git a/remix-debug/src/decoder/types/DynamicByteArray.js b/remix-debug/src/decoder/types/DynamicByteArray.js
new file mode 100644
index 0000000000..d879b5ef6a
--- /dev/null
+++ b/remix-debug/src/decoder/types/DynamicByteArray.js
@@ -0,0 +1,80 @@
+'use strict'
+var util = require('./util')
+var remixLib = require('remix-lib')
+var sha3256 = remixLib.util.sha3_256
+var BN = require('ethereumjs-util').BN
+var RefType = require('./RefType')
+
+class DynamicByteArray extends RefType {
+ constructor (location) {
+ super(1, 32, 'bytes', location)
+ }
+
+ async decodeFromStorage (location, storageResolver) {
+ var value = '0x0'
+ try {
+ value = await util.extractHexValue(location, storageResolver, this.storageBytes)
+ } catch (e) {
+ console.log(e)
+ return {
+ value: '',
+ type: this.typeName
+ }
+ }
+ var bn = new BN(value, 16)
+ if (bn.testn(0)) {
+ var length = bn.div(new BN(2))
+ var dataPos = new BN(sha3256(location.slot).replace('0x', ''), 16)
+ var ret = ''
+ var currentSlot = '0x'
+ try {
+ currentSlot = await util.readFromStorage(dataPos, storageResolver)
+ } catch (e) {
+ console.log(e)
+ return {
+ value: '',
+ type: this.typeName
+ }
+ }
+ while (length.gt(ret.length) && ret.length < 32000) {
+ currentSlot = currentSlot.replace('0x', '')
+ ret += currentSlot
+ dataPos = dataPos.add(new BN(1))
+ try {
+ currentSlot = await util.readFromStorage(dataPos, storageResolver)
+ } catch (e) {
+ console.log(e)
+ return {
+ value: '',
+ type: this.typeName
+ }
+ }
+ }
+ return {
+ value: '0x' + ret.replace(/(00)+$/, ''),
+ length: '0x' + length.toString(16),
+ type: this.typeName
+ }
+ } else {
+ var size = parseInt(value.substr(value.length - 2, 2), 16) / 2
+ return {
+ value: '0x' + value.substr(0, size * 2),
+ length: '0x' + size.toString(16),
+ type: this.typeName
+ }
+ }
+ }
+
+ decodeFromMemoryInternal (offset, memory) {
+ offset = 2 * offset
+ var length = memory.substr(offset, 64)
+ length = 2 * parseInt(length, 16)
+ return {
+ length: '0x' + length.toString(16),
+ value: '0x' + memory.substr(offset + 64, length),
+ type: this.typeName
+ }
+ }
+}
+
+module.exports = DynamicByteArray
diff --git a/remix-debug/src/decoder/types/Enum.js b/remix-debug/src/decoder/types/Enum.js
new file mode 100644
index 0000000000..a170b61c16
--- /dev/null
+++ b/remix-debug/src/decoder/types/Enum.js
@@ -0,0 +1,30 @@
+'use strict'
+var ValueType = require('./ValueType')
+
+class Enum extends ValueType {
+ constructor (enumDef) {
+ var storageBytes = 0
+ var length = enumDef.children.length
+ while (length > 1) {
+ length = length / 256
+ storageBytes++
+ }
+ super(1, storageBytes, 'enum')
+ this.enumDef = enumDef
+ }
+
+ decodeValue (value) {
+ if (!value) {
+ return this.enumDef.children[0].attributes.name
+ } else {
+ value = parseInt(value, 16)
+ if (this.enumDef.children.length > value) {
+ return this.enumDef.children[value].attributes.name
+ } else {
+ return 'INVALID_ENUM<' + value + '>'
+ }
+ }
+ }
+}
+
+module.exports = Enum
diff --git a/remix-debug/src/decoder/types/FixedByteArray.js b/remix-debug/src/decoder/types/FixedByteArray.js
new file mode 100644
index 0000000000..3cfe1a7e4c
--- /dev/null
+++ b/remix-debug/src/decoder/types/FixedByteArray.js
@@ -0,0 +1,14 @@
+'use strict'
+var ValueType = require('./ValueType')
+
+class FixedByteArray extends ValueType {
+ constructor (storageBytes) {
+ super(1, storageBytes, 'bytes' + storageBytes)
+ }
+
+ decodeValue (value) {
+ return '0x' + value.substr(0, 2 * this.storageBytes).toUpperCase()
+ }
+}
+
+module.exports = FixedByteArray
diff --git a/remix-debug/src/decoder/types/Int.js b/remix-debug/src/decoder/types/Int.js
new file mode 100644
index 0000000000..6288899e75
--- /dev/null
+++ b/remix-debug/src/decoder/types/Int.js
@@ -0,0 +1,16 @@
+'use strict'
+var util = require('./util')
+var ValueType = require('./ValueType')
+
+class Int extends ValueType {
+ constructor (storageBytes) {
+ super(1, storageBytes, 'int' + storageBytes * 8)
+ }
+
+ decodeValue (value) {
+ value = util.extractHexByteSlice(value, this.storageBytes, 0)
+ return util.decodeIntFromHex(value, this.storageBytes, true)
+ }
+}
+
+module.exports = Int
diff --git a/remix-debug/src/decoder/types/Mapping.js b/remix-debug/src/decoder/types/Mapping.js
new file mode 100644
index 0000000000..d190bbb459
--- /dev/null
+++ b/remix-debug/src/decoder/types/Mapping.js
@@ -0,0 +1,87 @@
+'use strict'
+var RefType = require('./RefType')
+var util = require('./util')
+var ethutil = require('ethereumjs-util')
+
+class Mapping extends RefType {
+ constructor (underlyingTypes, location, fullType) {
+ super(1, 32, fullType, 'storage')
+ this.keyType = underlyingTypes.keyType
+ this.valueType = underlyingTypes.valueType
+ this.initialDecodedState = null
+ }
+
+ async decodeFromStorage (location, storageResolver) {
+ if (!this.initialDecodedState) { // cache the decoded initial storage
+ var mappingsInitialPreimages
+ try {
+ mappingsInitialPreimages = await storageResolver.initialMappingsLocation()
+ this.initialDecodedState = await this.decodeMappingsLocation(mappingsInitialPreimages, location, storageResolver)
+ } catch (e) {
+ return {
+ value: e.message,
+ type: this.typeName
+ }
+ }
+ }
+ var mappingPreimages = await storageResolver.mappingsLocation()
+ var ret = await this.decodeMappingsLocation(mappingPreimages, location, storageResolver) // fetch mapping storage changes
+ ret = Object.assign({}, this.initialDecodedState, ret) // merge changes
+ return {
+ value: ret,
+ type: this.typeName
+ }
+ }
+
+ decodeFromMemoryInternal (offset, memory) {
+ // mappings can only exist in storage and not in memory
+ // so this should never be called
+ return {
+ value: '',
+ length: '0x',
+ type: this.typeName
+ }
+ }
+
+ async decodeMappingsLocation (preimages, location, storageResolver) {
+ var mapSlot = util.normalizeHex(ethutil.bufferToHex(location.slot))
+ if (!preimages[mapSlot]) {
+ return {}
+ }
+ var ret = {}
+ for (var i in preimages[mapSlot]) {
+ var mapLocation = getMappingLocation(i, location.slot)
+ var globalLocation = {
+ offset: location.offset,
+ slot: mapLocation
+ }
+ ret[i] = await this.valueType.decodeFromStorage(globalLocation, storageResolver)
+ console.log('global location', globalLocation, i, ret[i])
+ }
+ return ret
+ }
+}
+
+function getMappingLocation (key, position) {
+ // mapping storage location decribed at http://solidity.readthedocs.io/en/develop/miscellaneous.html#layout-of-state-variables-in-storage
+ // > the value corresponding to a mapping key k is located at keccak256(k . p) where . is concatenation.
+
+ // key should be a hex string, and position an int
+ var mappingK = ethutil.toBuffer('0x' + key)
+ var mappingP = ethutil.intToBuffer(position)
+ mappingP = ethutil.setLengthLeft(mappingP, 32)
+ var mappingKeyBuf = concatTypedArrays(mappingK, mappingP)
+ var mappingKeyPreimage = '0x' + mappingKeyBuf.toString('hex')
+ var mappingStorageLocation = ethutil.sha3(mappingKeyPreimage)
+ mappingStorageLocation = new ethutil.BN(mappingStorageLocation, 16)
+ return mappingStorageLocation
+}
+
+function concatTypedArrays (a, b) { // a, b TypedArray of same type
+ let c = new (a.constructor)(a.length + b.length)
+ c.set(a, 0)
+ c.set(b, a.length)
+ return c
+}
+
+module.exports = Mapping
diff --git a/remix-debug/src/decoder/types/RefType.js b/remix-debug/src/decoder/types/RefType.js
new file mode 100644
index 0000000000..c33a3d18a1
--- /dev/null
+++ b/remix-debug/src/decoder/types/RefType.js
@@ -0,0 +1,84 @@
+'use strict'
+var util = require('./util')
+
+class RefType {
+ constructor (storageSlots, storageBytes, typeName, location) {
+ this.location = location
+ this.storageSlots = storageSlots
+ this.storageBytes = storageBytes
+ this.typeName = typeName
+ this.basicType = 'RefType'
+ }
+
+ /**
+ * decode the type from the stack
+ *
+ * @param {Int} stackDepth - position of the type in the stack
+ * @param {Array} stack - stack
+ * @param {String} - memory
+ * @param {Object} - storageResolver
+ * @return {Object} decoded value
+ */
+ async decodeFromStack (stackDepth, stack, memory, storageResolver) {
+ if (stack.length - 1 < stackDepth) {
+ return {
+ error: '',
+ type: this.typeName
+ }
+ }
+ var offset = stack[stack.length - 1 - stackDepth]
+ if (this.isInStorage()) {
+ offset = util.toBN(offset)
+ try {
+ return await this.decodeFromStorage({ offset: 0, slot: offset }, storageResolver)
+ } catch (e) {
+ console.log(e)
+ return {
+ error: '',
+ type: this.typeName
+ }
+ }
+ } else if (this.isInMemory()) {
+ offset = parseInt(offset, 16)
+ return this.decodeFromMemoryInternal(offset, memory)
+ } else {
+ return {
+ error: '',
+ type: this.typeName
+ }
+ }
+ }
+
+ /**
+ * decode the type from the memory
+ *
+ * @param {Int} offset - position of the ref of the type in memory
+ * @param {String} memory - memory
+ * @return {Object} decoded value
+ */
+ decodeFromMemory (offset, memory) {
+ offset = memory.substr(2 * offset, 64)
+ offset = parseInt(offset, 16)
+ return this.decodeFromMemoryInternal(offset, memory)
+ }
+
+ /**
+ * current type defined in storage
+ *
+ * @return {Bool} - return true if the type is defined in the storage
+ */
+ isInStorage () {
+ return this.location.indexOf('storage') === 0
+ }
+
+ /**
+ * current type defined in memory
+ *
+ * @return {Bool} - return true if the type is defined in the memory
+ */
+ isInMemory () {
+ return this.location.indexOf('memory') === 0
+ }
+}
+
+module.exports = RefType
diff --git a/remix-debug/src/decoder/types/StringType.js b/remix-debug/src/decoder/types/StringType.js
new file mode 100644
index 0000000000..f21eac4a51
--- /dev/null
+++ b/remix-debug/src/decoder/types/StringType.js
@@ -0,0 +1,56 @@
+'use strict'
+var DynamicBytes = require('./DynamicByteArray')
+
+class StringType extends DynamicBytes {
+ constructor (location) {
+ super(location)
+ this.typeName = 'string'
+ }
+
+ async decodeFromStorage (location, storageResolver) {
+ var decoded = '0x'
+ try {
+ decoded = await super.decodeFromStorage(location, storageResolver)
+ } catch (e) {
+ console.log(e)
+ return ''
+ }
+ return format(decoded)
+ }
+
+ async decodeFromStack (stackDepth, stack, memory) {
+ try {
+ return await super.decodeFromStack(stackDepth, stack, memory)
+ } catch (e) {
+ console.log(e)
+ return ''
+ }
+ }
+
+ decodeFromMemoryInternal (offset, memory) {
+ var decoded = super.decodeFromMemoryInternal(offset, memory)
+ return format(decoded)
+ }
+}
+
+function format (decoded) {
+ if (decoded.error) {
+ return decoded
+ }
+ var value = decoded.value
+ value = value.replace('0x', '').replace(/(..)/g, '%$1')
+ var ret = {
+ length: decoded.length,
+ raw: decoded.value,
+ type: 'string'
+ }
+ try {
+ ret.value = decodeURIComponent(value)
+ } catch (e) {
+ ret.error = 'Invalid UTF8 encoding'
+ ret.raw = decoded.value
+ }
+ return ret
+}
+
+module.exports = StringType
diff --git a/remix-debug/src/decoder/types/Struct.js b/remix-debug/src/decoder/types/Struct.js
new file mode 100644
index 0000000000..d715caf049
--- /dev/null
+++ b/remix-debug/src/decoder/types/Struct.js
@@ -0,0 +1,47 @@
+'use strict'
+var util = require('./util')
+var RefType = require('./RefType')
+
+class Struct extends RefType {
+ constructor (memberDetails, location, fullType) {
+ super(memberDetails.storageSlots, 32, 'struct ' + fullType, location)
+ this.members = memberDetails.members
+ }
+
+ async decodeFromStorage (location, storageResolver) {
+ var ret = {}
+ for (var k in this.members) {
+ var item = this.members[k]
+ var globalLocation = {
+ offset: location.offset + item.storagelocation.offset,
+ slot: util.add(location.slot, item.storagelocation.slot)
+ }
+ try {
+ ret[item.name] = await item.type.decodeFromStorage(globalLocation, storageResolver)
+ } catch (e) {
+ console.log(e)
+ ret[item.name] = ''
+ }
+ }
+ return {
+ value: ret,
+ type: this.typeName
+ }
+ }
+
+ decodeFromMemoryInternal (offset, memory) {
+ var ret = {}
+ this.members.map((item, i) => {
+ var contentOffset = offset
+ var member = item.type.decodeFromMemory(contentOffset, memory)
+ ret[item.name] = member
+ offset += 32
+ })
+ return {
+ value: ret,
+ type: this.typeName
+ }
+ }
+}
+
+module.exports = Struct
diff --git a/remix-debug/src/decoder/types/Uint.js b/remix-debug/src/decoder/types/Uint.js
new file mode 100644
index 0000000000..3bdc4b4976
--- /dev/null
+++ b/remix-debug/src/decoder/types/Uint.js
@@ -0,0 +1,16 @@
+'use strict'
+var util = require('./util')
+var ValueType = require('./ValueType')
+
+class Uint extends ValueType {
+ constructor (storageBytes) {
+ super(1, storageBytes, 'uint' + storageBytes * 8)
+ }
+
+ decodeValue (value) {
+ value = util.extractHexByteSlice(value, this.storageBytes, 0)
+ return util.decodeIntFromHex(value, this.storageBytes, false)
+ }
+}
+
+module.exports = Uint
diff --git a/remix-debug/src/decoder/types/ValueType.js b/remix-debug/src/decoder/types/ValueType.js
new file mode 100644
index 0000000000..44c4c7b536
--- /dev/null
+++ b/remix-debug/src/decoder/types/ValueType.js
@@ -0,0 +1,72 @@
+'use strict'
+var util = require('./util')
+
+class ValueType {
+ constructor (storageSlots, storageBytes, typeName) {
+ this.storageSlots = storageSlots
+ this.storageBytes = storageBytes
+ this.typeName = typeName
+ this.basicType = 'ValueType'
+ }
+
+ /**
+ * decode the type with the @arg location from the storage
+ *
+ * @param {Object} location - containing offset and slot
+ * @param {Object} storageResolver - resolve storage queries
+ * @return {Object} - decoded value
+ */
+ async decodeFromStorage (location, storageResolver) {
+ try {
+ var value = await util.extractHexValue(location, storageResolver, this.storageBytes)
+ return {
+ value: this.decodeValue(value),
+ type: this.typeName
+ }
+ } catch (e) {
+ console.log(e)
+ return {
+ value: '',
+ type: this.typeName
+ }
+ }
+ }
+
+ /**
+ * decode the type from the stack
+ *
+ * @param {Int} stackDepth - position of the type in the stack
+ * @param {Array} stack - stack
+ * @param {String} - memory
+ * @return {Object} - decoded value
+ */
+ async decodeFromStack (stackDepth, stack, memory) {
+ var value
+ if (stackDepth >= stack.length) {
+ value = this.decodeValue('')
+ } else {
+ value = this.decodeValue(stack[stack.length - 1 - stackDepth].replace('0x', ''))
+ }
+ return {
+ value: value,
+ type: this.typeName
+ }
+ }
+
+ /**
+ * decode the type with the @arg offset location from the memory
+ *
+ * @param {Int} stackDepth - position of the type in the stack
+ * @return {String} - memory
+ * @return {Object} - decoded value
+ */
+ decodeFromMemory (offset, memory) {
+ var value = memory.substr(2 * offset, 64)
+ return {
+ value: this.decodeValue(value),
+ type: this.typeName
+ }
+ }
+}
+
+module.exports = ValueType
diff --git a/remix-debug/src/decoder/types/util.js b/remix-debug/src/decoder/types/util.js
new file mode 100644
index 0000000000..c7d8503b9e
--- /dev/null
+++ b/remix-debug/src/decoder/types/util.js
@@ -0,0 +1,120 @@
+'use strict'
+var ethutil = require('ethereumjs-util')
+var BN = require('ethereumjs-util').BN
+
+module.exports = {
+ readFromStorage: readFromStorage,
+ decodeIntFromHex: decodeIntFromHex,
+ extractHexValue: extractHexValue,
+ extractHexByteSlice: extractHexByteSlice,
+ toBN: toBN,
+ add: add,
+ extractLocation: extractLocation,
+ removeLocation: removeLocation,
+ normalizeHex: normalizeHex,
+ extractLocationFromAstVariable: extractLocationFromAstVariable
+}
+
+function decodeIntFromHex (value, byteLength, signed) {
+ var bigNumber = new BN(value, 16)
+ if (signed) {
+ bigNumber = bigNumber.fromTwos(8 * byteLength)
+ }
+ return bigNumber.toString(10)
+}
+
+function readFromStorage (slot, storageResolver) {
+ var hexSlot = '0x' + normalizeHex(ethutil.bufferToHex(slot))
+ return new Promise((resolve, reject) => {
+ storageResolver.storageSlot(hexSlot, (error, slot) => {
+ if (error) {
+ return reject(error)
+ } else {
+ if (!slot) {
+ slot = {
+ key: slot,
+ value: ''
+ }
+ }
+ return resolve(normalizeHex(slot.value))
+ }
+ })
+ })
+}
+
+/**
+ * @returns a hex encoded byte slice of length @arg byteLength from inside @arg slotValue.
+ *
+ * @param {String} slotValue - hex encoded value to extract the byte slice from
+ * @param {Int} byteLength - Length of the byte slice to extract
+ * @param {Int} offsetFromLSB - byte distance from the right end slot value to the right end of the byte slice
+ */
+function extractHexByteSlice (slotValue, byteLength, offsetFromLSB) {
+ var offset = slotValue.length - 2 * offsetFromLSB - 2 * byteLength
+ return slotValue.substr(offset, 2 * byteLength)
+}
+
+/**
+ * @returns a hex encoded storage content at the given @arg location. it does not have Ox prefix but always has the full length.
+ *
+ * @param {Object} location - object containing the slot and offset of the data to extract.
+ * @param {Object} storageResolver - storage resolver
+ * @param {Int} byteLength - Length of the byte slice to extract
+ */
+async function extractHexValue (location, storageResolver, byteLength) {
+ var slotvalue
+ try {
+ slotvalue = await readFromStorage(location.slot, storageResolver)
+ } catch (e) {
+ console.log(e)
+ return '0x'
+ }
+ return extractHexByteSlice(slotvalue, byteLength, location.offset)
+}
+
+function toBN (value) {
+ if (value instanceof BN) {
+ return value
+ } else if (value.indexOf && value.indexOf('0x') === 0) {
+ value = ethutil.unpad(value.replace('Ox', ''))
+ value = new BN(value === '' ? '0' : value, 16)
+ } else if (!isNaN(value)) {
+ value = new BN(value)
+ }
+ return value
+}
+
+function add (value1, value2) {
+ return toBN(value1).add(toBN(value2))
+}
+
+function removeLocation (type) {
+ return type.replace(/( storage ref| storage pointer| memory| calldata)/g, '')
+}
+
+function extractLocation (type) {
+ var match = type.match(/( storage ref| storage pointer| memory| calldata)?$/)
+ if (match[1] !== '') {
+ return match[1].trim()
+ } else {
+ return null
+ }
+}
+
+function extractLocationFromAstVariable (node) {
+ if (node.attributes.storageLocation !== 'default') {
+ return node.attributes.storageLocation
+ } else if (node.attributes.stateVariable) {
+ return 'storage'
+ } else {
+ return 'default' // local variables => storage, function parameters & return values => memory, state => storage
+ }
+}
+
+function normalizeHex (hex) {
+ hex = hex.replace('0x', '')
+ if (hex.length < 64) {
+ return (new Array(64 - hex.length + 1).join('0')) + hex
+ }
+ return hex
+}
diff --git a/remix-debug/src/storage/mappingPreimages.js b/remix-debug/src/storage/mappingPreimages.js
new file mode 100644
index 0000000000..5f6d4a47dd
--- /dev/null
+++ b/remix-debug/src/storage/mappingPreimages.js
@@ -0,0 +1,53 @@
+
+module.exports = {
+ decodeMappingsKeys: decodeMappingsKeys
+}
+
+/**
+ * extract the mappings location from the storage
+ * like { "" : { "": preimageOf1 }, { "": preimageOf2 }, ... }
+ *
+ * @param {Object} storage - storage given by storage Viewer (basically a mapping hashedkey : {key, value})
+ * @param {Function} callback - calback
+ * @return {Map} - solidity mapping location (e.g { "" : { "": preimageOf1 }, { "": preimageOf2 }, ... })
+ */
+async function decodeMappingsKeys (web3, storage, callback) {
+ var ret = {}
+ for (var hashedLoc in storage) {
+ var preimage
+ try {
+ preimage = await getPreimage(web3, storage[hashedLoc].key)
+ } catch (e) {
+ }
+ if (preimage) {
+ // got preimage!
+ // get mapping position (i.e. storage slot), its the last 32 bytes
+ var slotByteOffset = preimage.length - 64
+ var mappingSlot = preimage.substr(slotByteOffset)
+ var mappingKey = preimage.substr(0, slotByteOffset)
+ if (!ret[mappingSlot]) {
+ ret[mappingSlot] = {}
+ }
+ ret[mappingSlot][mappingKey] = preimage
+ }
+ }
+ callback(null, ret)
+}
+
+/**
+ * Uses web3 to return preimage of a key
+ *
+ * @param {String} key - key to retrieve the preimage of
+ * @return {String} - preimage of the given key
+ */
+function getPreimage (web3, key) {
+ return new Promise((resolve, reject) => {
+ web3.debug.preimage(key.indexOf('0x') === 0 ? key : '0x' + key, function (error, preimage) {
+ if (error) {
+ resolve(null)
+ } else {
+ resolve(preimage)
+ }
+ })
+ })
+}
diff --git a/remix-debug/src/storage/storageResolver.js b/remix-debug/src/storage/storageResolver.js
new file mode 100644
index 0000000000..245a73b6a2
--- /dev/null
+++ b/remix-debug/src/storage/storageResolver.js
@@ -0,0 +1,166 @@
+'use strict'
+var remixLib = require('remix-lib')
+var traceHelper = remixLib.helpers.trace
+var mappingPreimages = require('./mappingPreimages')
+
+/**
+ * Basically one instance is created for one debugging session.
+ * (TODO: one instance need to be shared over all the components)
+ */
+class StorageResolver {
+ constructor (options) {
+ this.storageByAddress = {}
+ this.preimagesMappingByAddress = {}
+ this.maxSize = 100
+ this.web3 = options.web3
+ this.zeroSlot = '0x0000000000000000000000000000000000000000000000000000000000000000'
+ }
+
+ /**
+ * returns the storage for the given context (address and vm trace index)
+ * returns the range 0x0 => this.maxSize
+ *
+ * @param {Object} - tx - transaction
+ * @param {Int} - stepIndex - Index of the stop in the vm trace
+ * @param {String} - address - lookup address
+ * @param {Function} - callback - contains a map: [hashedKey] = {key, hashedKey, value}
+ */
+ storageRange (tx, stepIndex, address, callback) {
+ this.storageRangeInternal(this, this.zeroSlot, tx, stepIndex, address, callback)
+ }
+
+ /**
+ * compute the mappgings type locations for the current address (cached for a debugging session)
+ * note: that only retrieve the first 100 items.
+ *
+ * @param {String} address - contract address
+ * @param {Object} address - storage
+ * @return {Function} - callback
+ */
+ initialPreimagesMappings (tx, stepIndex, address, callback) {
+ const self = this
+ if (this.preimagesMappingByAddress[address]) {
+ return callback(null, this.preimagesMappingByAddress[address])
+ }
+ this.storageRange(tx, stepIndex, address, (error, storage) => {
+ if (error) {
+ return callback(error)
+ }
+ mappingPreimages.decodeMappingsKeys(self.web3, storage, (error, mappings) => {
+ if (error) {
+ callback(error)
+ } else {
+ this.preimagesMappingByAddress[address] = mappings
+ callback(null, mappings)
+ }
+ })
+ })
+ }
+
+ /**
+ * return a slot value for the given context (address and vm trace index)
+ *
+ * @param {String} - slot - slot key
+ * @param {Object} - tx - transaction
+ * @param {Int} - stepIndex - Index of the stop in the vm trace
+ * @param {String} - address - lookup address
+ * @param {Function} - callback - {key, hashedKey, value} -
+ */
+ storageSlot (slot, tx, stepIndex, address, callback) {
+ this.storageRangeInternal(this, slot, tx, stepIndex, address, function (error, storage) {
+ if (error) {
+ callback(error)
+ } else {
+ callback(null, storage[slot] !== undefined ? storage[slot] : null)
+ }
+ })
+ }
+
+ /**
+ * return True if the storage at @arg address is complete
+ *
+ * @param {String} address - contract address
+ * @return {Bool} - return True if the storage at @arg address is complete
+ */
+ isComplete (address) {
+ return this.storageByAddress[address] && this.storageByAddress[address].complete
+ }
+
+ /**
+ * retrieve the storage and ensure at least @arg slot is cached.
+ * - If @arg slot is already cached, the storage will be returned from the cache
+ * even if the next 1000 items are not in the cache.
+ * - If @arg slot is not cached, the corresponding value will be resolved and the next 1000 slots.
+ */
+ storageRangeInternal (self, slotKey, tx, stepIndex, address, callback) {
+ var cached = this.fromCache(self, address)
+ if (cached && cached.storage[slotKey]) { // we have the current slot in the cache and maybe the next 1000...
+ return callback(null, cached.storage)
+ }
+ this.storageRangeWeb3Call(tx, address, slotKey, self.maxSize, (error, storage, nextKey) => {
+ if (error) {
+ return callback(error)
+ }
+ if (!storage[slotKey] && slotKey !== self.zeroSlot) { // we don't cache the zero slot (could lead to inconsistency)
+ storage[slotKey] = {
+ key: slotKey,
+ value: self.zeroSlot
+ }
+ }
+ self.toCache(self, address, storage)
+ if (slotKey === self.zeroSlot && !nextKey) { // only working if keys are sorted !!
+ self.storageByAddress[address].complete = true
+ }
+ callback(null, storage)
+ })
+ }
+
+ /**
+ * retrieve the storage from the cache. if @arg slot is defined, return only the desired slot, if not return the entire known storage
+ *
+ * @param {String} address - contract address
+ * @return {String} - either the entire known storage or a single value
+ */
+ fromCache (self, address) {
+ if (!self.storageByAddress[address]) {
+ return null
+ }
+ return self.storageByAddress[address]
+ }
+
+ /**
+ * store the result of `storageRangeAtInternal`
+ *
+ * @param {String} address - contract address
+ * @param {Object} storage - result of `storageRangeAtInternal`, contains {key, hashedKey, value}
+ */
+ toCache (self, address, storage) {
+ if (!self.storageByAddress[address]) {
+ self.storageByAddress[address] = {}
+ }
+ self.storageByAddress[address].storage = Object.assign(self.storageByAddress[address].storage || {}, storage)
+ }
+
+ storageRangeWeb3Call (tx, address, start, maxSize, callback) {
+ if (traceHelper.isContractCreation(address)) {
+ callback(null, {}, null)
+ } else {
+ this.web3.debug.storageRangeAt(
+ tx.blockHash, tx.transactionIndex === undefined ? tx.hash : tx.transactionIndex,
+ address,
+ start,
+ maxSize,
+ (error, result) => {
+ if (error) {
+ callback(error)
+ } else if (result.storage) {
+ callback(null, result.storage, result.nextKey)
+ } else {
+ callback('the storage has not been provided')
+ }
+ })
+ }
+ }
+}
+
+module.exports = StorageResolver
diff --git a/remix-debug/src/storage/storageViewer.js b/remix-debug/src/storage/storageViewer.js
new file mode 100644
index 0000000000..21af4d5f38
--- /dev/null
+++ b/remix-debug/src/storage/storageViewer.js
@@ -0,0 +1,132 @@
+'use strict'
+var remixLib = require('remix-lib')
+var util = remixLib.util
+var mappingPreimages = require('./mappingPreimages')
+
+ /**
+ * easier access to the storage resolver
+ * Basically one instance is created foreach execution step and foreach component that need it.
+ * (TODO: one instance need to be shared over all the components)
+ */
+class StorageViewer {
+ constructor (_context, _storageResolver, _traceManager) {
+ this.context = _context
+ this.storageResolver = _storageResolver
+ this.web3 = this.storageResolver.web3
+ this.initialMappingsLocationPromise = null
+ this.currentMappingsLocationPromise = null
+ _traceManager.accumulateStorageChanges(this.context.stepIndex, this.context.address, {}, (error, storageChanges) => {
+ if (!error) {
+ this.storageChanges = storageChanges
+ } else {
+ console.log(error)
+ }
+ })
+ }
+
+ /**
+ * return the storage for the current context (address and vm trace index)
+ * by default now returns the range 0 => 1000
+ *
+ * @param {Function} - callback - contains a map: [hashedKey] = {key, hashedKey, value}
+ */
+ storageRange (callback) {
+ this.storageResolver.storageRange(this.context.tx, this.context.stepIndex, this.context.address, (error, storage) => {
+ if (error) {
+ callback(error)
+ } else {
+ callback(null, Object.assign({}, storage, this.storageChanges))
+ }
+ })
+ }
+
+ /**
+ * return a slot value for the current context (address and vm trace index)
+ * @param {String} - slot - slot key (not hashed key!)
+ * @param {Function} - callback - {key, hashedKey, value} -
+ */
+ storageSlot (slot, callback) {
+ var hashed = util.sha3_256(slot)
+ if (this.storageChanges[hashed]) {
+ return callback(null, this.storageChanges[hashed])
+ }
+ this.storageResolver.storageSlot(hashed, this.context.tx, this.context.stepIndex, this.context.address, (error, storage) => {
+ if (error) {
+ callback(error)
+ } else {
+ callback(null, storage)
+ }
+ })
+ }
+
+ /**
+ * return True if the storage at @arg address is complete
+ *
+ * @param {String} address - contract address
+ * @return {Bool} - return True if the storage at @arg address is complete
+ */
+ isComplete (address) {
+ return this.storageResolver.isComplete(address)
+ }
+
+ /**
+ * return all the possible mappings locations for the current context (cached) do not return state changes during the current transaction
+ *
+ * @param {Function} callback
+ */
+ async initialMappingsLocation () {
+ if (!this.initialMappingsLocationPromise) {
+ this.initialMappingsLocationPromise = new Promise((resolve, reject) => {
+ this.storageResolver.initialPreimagesMappings(this.context.tx, this.context.stepIndex, this.context.address, (error, initialMappingsLocation) => {
+ if (error) {
+ reject(error)
+ } else {
+ resolve(initialMappingsLocation)
+ }
+ })
+ })
+ }
+ return this.initialMappingsLocationPromise
+ }
+
+ /**
+ * return all the possible mappings locations for the current context (cached) and current mapping slot. returns state changes during the current transaction
+ *
+ * @param {Function} callback
+ */
+ async mappingsLocation () {
+ if (!this.currentMappingsLocationPromise) {
+ this.currentMappingsLocationPromise = new Promise((resolve, reject) => {
+ this.extractMappingsLocationChanges(this.storageChanges, (error, mappingsLocationChanges) => {
+ if (error) {
+ reject(error)
+ } else {
+ resolve(mappingsLocationChanges)
+ }
+ })
+ })
+ }
+ return this.currentMappingsLocationPromise
+ }
+
+ /**
+ * retrieve mapping location changes from the storage changes.
+ *
+ * @param {Function} callback
+ */
+ extractMappingsLocationChanges (storageChanges, callback) {
+ if (this.mappingsLocationChanges) {
+ return callback(null, this.mappingsLocationChanges)
+ }
+ mappingPreimages.decodeMappingsKeys(this.web3, storageChanges, (error, mappings) => {
+ if (!error) {
+ this.mappingsLocationChanges = mappings
+ return callback(null, this.mappingsLocationChanges)
+ } else {
+ callback(error)
+ }
+ })
+ }
+}
+
+module.exports = StorageViewer
diff --git a/src/trace/traceAnalyser.js b/remix-debug/src/trace/traceAnalyser.js
similarity index 80%
rename from src/trace/traceAnalyser.js
rename to remix-debug/src/trace/traceAnalyser.js
index fbeafabb4e..240e1ee3dd 100644
--- a/src/trace/traceAnalyser.js
+++ b/remix-debug/src/trace/traceAnalyser.js
@@ -1,5 +1,6 @@
'use strict'
-var traceHelper = require('../helpers/traceHelper')
+var remixLib = require('remix-lib')
+var traceHelper = remixLib.helpers.trace
function TraceAnalyser (_cache) {
this.traceCache = _cache
@@ -10,17 +11,12 @@ TraceAnalyser.prototype.analyse = function (trace, tx, callback) {
this.trace = trace
this.traceCache.pushStoreChanges(0, tx.to)
var context = {
- currentStorageAddress: tx.to,
- previousStorageAddress: tx.to,
+ storageContext: [tx.to],
currentCallIndex: 0,
lastCallIndex: 0
}
var callStack = [tx.to]
- this.traceCache.pushCallChanges(0, 0, callStack[0])
- this.traceCache.pushCallStack(0, {
- callStack: callStack.slice(0)
- })
-
+ this.traceCache.pushCall(trace[0], 0, callStack[0], callStack.slice(0))
if (traceHelper.isContractCreation(tx.to)) {
this.traceCache.pushContractCreation(tx.to, tx.input)
}
@@ -80,16 +76,16 @@ TraceAnalyser.prototype.buildStorage = function (index, step, context) {
if (traceHelper.newContextStorage(step) && !traceHelper.isCallToPrecompiledContract(index, this.trace)) {
var calledAddress = traceHelper.resolveCalledAddress(index, this.trace)
if (calledAddress) {
- context.currentStorageAddress = calledAddress
+ context.storageContext.push(calledAddress)
} else {
console.log('unable to build storage changes. ' + index + ' does not match with a CALL. storage changes will be corrupted')
}
- this.traceCache.pushStoreChanges(index + 1, context.currentStorageAddress)
+ this.traceCache.pushStoreChanges(index + 1, context.storageContext[context.storageContext.length - 1])
} else if (traceHelper.isSSTOREInstruction(step)) {
- this.traceCache.pushStoreChanges(index + 1, context.currentStorageAddress, step.stack[step.stack.length - 1], step.stack[step.stack.length - 2])
+ this.traceCache.pushStoreChanges(index + 1, context.storageContext[context.storageContext.length - 1], step.stack[step.stack.length - 1], step.stack[step.stack.length - 2])
} else if (traceHelper.isReturnInstruction(step)) {
- context.currentStorageAddress = context.previousStorageAddress
- this.traceCache.pushStoreChanges(index + 1, context.currentStorageAddress)
+ context.storageContext.pop()
+ this.traceCache.pushStoreChanges(index + 1, context.storageContext[context.storageContext.length - 1])
}
return context
}
@@ -110,21 +106,15 @@ TraceAnalyser.prototype.buildDepth = function (index, step, tx, callStack, conte
console.log('unable to build depth changes. ' + index + ' does not match with a CALL. depth changes will be corrupted')
}
}
- this.traceCache.pushCallChanges(step, index + 1, newAddress)
- this.traceCache.pushCallStack(index + 1, {
- callStack: callStack.slice(0)
- })
+ this.traceCache.pushCall(step, index + 1, newAddress, callStack.slice(0))
this.buildCalldata(index, step, tx, true)
this.traceCache.pushSteps(index, context.currentCallIndex)
context.lastCallIndex = context.currentCallIndex
context.currentCallIndex = 0
- } else if (traceHelper.isReturnInstruction(step)) {
- if (index + 1 < this.trace.length) {
+ } else if (traceHelper.isReturnInstruction(step) || traceHelper.isStopInstruction(step) || step.error || step.invalidDepthChange) {
+ if (index < this.trace.length) {
callStack.pop()
- this.traceCache.pushCallChanges(step, index + 1)
- this.traceCache.pushCallStack(index + 1, {
- callStack: callStack.slice(0)
- })
+ this.traceCache.pushCall(step, index + 1, null, callStack.slice(0), step.error || step.invalidDepthChange)
this.buildCalldata(index, step, tx, false)
this.traceCache.pushSteps(index, context.currentCallIndex)
context.currentCallIndex = context.lastCallIndex + 1
diff --git a/src/trace/traceCache.js b/remix-debug/src/trace/traceCache.js
similarity index 55%
rename from src/trace/traceCache.js
rename to remix-debug/src/trace/traceCache.js
index 040c0919bb..6dc98a16b2 100644
--- a/src/trace/traceCache.js
+++ b/remix-debug/src/trace/traceCache.js
@@ -1,4 +1,7 @@
'use strict'
+var remixLib = require('remix-lib')
+var helper = remixLib.util
+
function TraceCache () {
this.init()
}
@@ -7,8 +10,8 @@ TraceCache.prototype.init = function () {
// ...Changes contains index in the vmtrace of the corresponding changes
this.returnValues = {}
- this.callChanges = []
- this.calls = {}
+ this.currentCall = null
+ this.callsTree = null
this.callsData = {}
this.contractCreation = {}
this.steps = {}
@@ -18,7 +21,6 @@ TraceCache.prototype.init = function () {
this.memoryChanges = []
this.storageChanges = []
this.sstore = {} // all sstore occurence in the trace
- this.callStack = {} // contains all callStack by vmtrace index (we need to rebuild it, callstack is not included in the vmtrace)
}
TraceCache.prototype.pushSteps = function (index, currentCallIndex) {
@@ -34,11 +36,34 @@ TraceCache.prototype.pushMemoryChanges = function (value) {
this.memoryChanges.push(value)
}
-TraceCache.prototype.pushCallChanges = function (step, value, address) {
- this.callChanges.push(value)
- this.calls[value] = {
- op: step.op,
- address: address
+// outOfGas has been removed because gas left logging is apparently made differently
+// in the vm/geth/eth. TODO add the error property (with about the error in all clients)
+TraceCache.prototype.pushCall = function (step, index, address, callStack, reverted) {
+ var validReturnStep = step.op === 'RETURN' || step.op === 'STOP'
+ if (validReturnStep || reverted) {
+ if (this.currentCall) {
+ this.currentCall.call.return = index - 1
+ if (!validReturnStep) {
+ this.currentCall.call.reverted = reverted
+ }
+ var parent = this.currentCall.parent
+ this.currentCall = parent ? { call: parent.call, parent: parent.parent } : null
+ }
+ } else {
+ var call = {
+ op: step.op,
+ address: address,
+ callStack: callStack,
+ calls: {},
+ start: index
+ }
+ this.addresses.push(address)
+ if (this.currentCall) {
+ this.currentCall.call.calls[index] = call
+ } else {
+ this.callsTree = { call: call }
+ }
+ this.currentCall = { call: call, parent: this.currentCall }
}
}
@@ -58,31 +83,32 @@ TraceCache.prototype.pushContractCreation = function (token, code) {
this.contractCreation[token] = code
}
-TraceCache.prototype.pushCallStack = function (index, callStack) {
- this.callStack[index] = callStack
-}
-
TraceCache.prototype.pushStoreChanges = function (index, address, key, value) {
this.sstore[index] = {
'address': address,
'key': key,
- 'value': value
+ 'value': value,
+ 'hashedKey': helper.sha3_256(key)
}
this.storageChanges.push(index)
}
-TraceCache.prototype.rebuildStorage = function (address, storage, index) {
+TraceCache.prototype.accumulateStorageChanges = function (index, address, storage) {
+ var ret = Object.assign({}, storage)
for (var k in this.storageChanges) {
var changesIndex = this.storageChanges[k]
if (changesIndex > index) {
- return storage
+ return ret
}
var sstore = this.sstore[changesIndex]
if (sstore.address === address && sstore.key) {
- storage[sstore.key] = sstore.value
+ ret[sstore.hashedKey] = {
+ key: sstore.key,
+ value: sstore.value
+ }
}
}
- return storage
+ return ret
}
module.exports = TraceCache
diff --git a/src/trace/traceManager.js b/remix-debug/src/trace/traceManager.js
similarity index 67%
rename from src/trace/traceManager.js
rename to remix-debug/src/trace/traceManager.js
index 8d9215fd77..8db9b95157 100644
--- a/src/trace/traceManager.js
+++ b/remix-debug/src/trace/traceManager.js
@@ -3,15 +3,17 @@ var TraceAnalyser = require('./traceAnalyser')
var TraceRetriever = require('./traceRetriever')
var TraceCache = require('./traceCache')
var TraceStepManager = require('./traceStepManager')
-var traceHelper = require('../helpers/traceHelper')
-var util = require('../helpers/global')
+var remixLib = require('remix-lib')
+var traceHelper = remixLib.helpers.trace
+var util = remixLib.util
-function TraceManager () {
+function TraceManager (options) {
+ this.web3 = options.web3
this.isLoading = false
this.trace = null
this.traceCache = new TraceCache()
this.traceAnalyser = new TraceAnalyser(this.traceCache)
- this.traceRetriever = new TraceRetriever()
+ this.traceRetriever = new TraceRetriever({web3: this.web3})
this.traceStepManager = new TraceStepManager(this.traceAnalyser)
this.tx
}
@@ -20,7 +22,7 @@ function TraceManager () {
TraceManager.prototype.resolveTrace = function (tx, callback) {
this.tx = tx
this.init()
- if (!util.web3) callback('web3 not loaded', false)
+ if (!this.web3) callback('web3 not loaded', false)
this.isLoading = true
var self = this
this.traceRetriever.getTrace(tx.hash, function (error, result) {
@@ -73,48 +75,13 @@ TraceManager.prototype.getLength = function (callback) {
}
}
-TraceManager.prototype.getStorageAt = function (stepIndex, tx, callback, address) {
- var check = this.checkRequestedStep(stepIndex)
- if (check) {
- return callback(check, null)
- }
- if (!address) {
- var stoChange = traceHelper.findLowerBound(stepIndex, this.traceCache.storageChanges)
- if (stoChange === undefined) return callback('no storage found', null)
- address = this.traceCache.sstore[stoChange].address
- }
- var storage = {}
- storage = this.traceCache.rebuildStorage(address, storage, stepIndex)
+TraceManager.prototype.accumulateStorageChanges = function (index, address, storageOrigin, callback) {
+ var storage = this.traceCache.accumulateStorageChanges(index, address, storageOrigin)
callback(null, storage)
- /*
- // TODO: use it if we need the full storage to be loaded
- var self = this
- if (this.traceRetriever.debugStorageAtAvailable()) {
- var address = this.traceCache.sstore[stoChange].address
- this.traceRetriever.getStorage(tx, address, function (error, result) {
- if (error) {
- console.log(error)
- callback(error, null)
- } else {
- var storage = self.traceCache.rebuildStorage(address, result, stepIndex)
- callback(null, storage)
- }
- })
- } else {
- callback(null, this.trace[stoChange].storage)
- }
- */
}
TraceManager.prototype.getAddresses = function (callback) {
- var addresses = [ this.tx.to ]
- for (var k in this.traceCache.calls) {
- var address = this.traceCache.calls[k].address
- if (address && addresses.join('').indexOf(address) === -1) {
- addresses.push(address)
- }
- }
- callback(null, addresses)
+ callback(null, this.traceCache.addresses)
}
TraceManager.prototype.getCallDataAt = function (stepIndex, callback) {
@@ -122,19 +89,29 @@ TraceManager.prototype.getCallDataAt = function (stepIndex, callback) {
if (check) {
return callback(check, null)
}
- var callDataChange = traceHelper.findLowerBound(stepIndex, this.traceCache.callDataChanges)
- if (callDataChange === undefined) return callback('no calldata found', null)
+ var callDataChange = util.findLowerBoundValue(stepIndex, this.traceCache.callDataChanges)
+ if (callDataChange === null) return callback('no calldata found', null)
callback(null, [this.traceCache.callsData[callDataChange]])
}
+TraceManager.prototype.buildCallPath = function (stepIndex, callback) {
+ var check = this.checkRequestedStep(stepIndex)
+ if (check) {
+ return callback(check, null)
+ }
+ var callsPath = util.buildCallPath(stepIndex, this.traceCache.callsTree.call)
+ if (callsPath === null) return callback('no call path built', null)
+ callback(null, callsPath)
+}
+
TraceManager.prototype.getCallStackAt = function (stepIndex, callback) {
var check = this.checkRequestedStep(stepIndex)
if (check) {
return callback(check, null)
}
- var callStackChange = traceHelper.findLowerBound(stepIndex, this.traceCache.callChanges)
- if (callStackChange === undefined) return callback('no callstack found', null)
- callback(null, this.traceCache.callStack[callStackChange].callStack)
+ var call = util.findCall(stepIndex, this.traceCache.callsTree.call)
+ if (call === null) return callback('no callstack found', null)
+ callback(null, call.callStack)
}
TraceManager.prototype.getStackAt = function (stepIndex, callback) {
@@ -157,8 +134,8 @@ TraceManager.prototype.getLastCallChangeSince = function (stepIndex, callback) {
if (check) {
return callback(check, null)
}
- var callChange = traceHelper.findLowerBound(stepIndex, this.traceCache.callChanges)
- if (callChange === undefined) {
+ var callChange = util.findCall(stepIndex, this.traceCache.callsTree.call)
+ if (callChange === null) {
callback(null, 0)
} else {
callback(null, callChange)
@@ -170,21 +147,14 @@ TraceManager.prototype.getCurrentCalledAddressAt = function (stepIndex, callback
if (check) {
return callback(check, null)
}
- var self = this
- this.getLastCallChangeSince(stepIndex, function (error, addressIndex) {
+ this.getLastCallChangeSince(stepIndex, function (error, resp) {
if (error) {
callback(error, null)
} else {
- if (addressIndex === 0) {
- callback(null, self.tx.to)
+ if (resp) {
+ callback(null, resp.address)
} else {
- var callStack = self.traceCache.callStack[addressIndex].callStack
- var calledAddress = callStack[callStack.length - 1]
- if (calledAddress) {
- callback(null, calledAddress)
- } else {
- callback('unable to get current called address. ' + stepIndex + ' does not match with a CALL', null)
- }
+ callback('unable to get current called address. ' + stepIndex + ' does not match with a CALL')
}
}
})
@@ -203,8 +173,8 @@ TraceManager.prototype.getMemoryAt = function (stepIndex, callback) {
if (check) {
return callback(check, null)
}
- var lastChanges = traceHelper.findLowerBound(stepIndex, this.traceCache.memoryChanges)
- if (lastChanges === undefined) return callback('no memory found', null)
+ var lastChanges = util.findLowerBoundValue(stepIndex, this.traceCache.memoryChanges)
+ if (lastChanges === null) return callback('no memory found', null)
callback(null, this.trace[lastChanges].memory)
}
@@ -221,7 +191,11 @@ TraceManager.prototype.getReturnValue = function (stepIndex, callback) {
if (check) {
return callback(check, null)
}
- callback(null, this.traceCache.returnValues[stepIndex])
+ if (!this.traceCache.returnValues[stepIndex]) {
+ callback('current step is not a return step')
+ } else {
+ callback(null, this.traceCache.returnValues[stepIndex])
+ }
}
TraceManager.prototype.getCurrentStep = function (stepIndex, callback) {
@@ -269,18 +243,14 @@ TraceManager.prototype.findStepOverForward = function (currentStep) {
return this.traceStepManager.findStepOverForward(currentStep)
}
-TraceManager.prototype.findStepOutBack = function (currentStep) {
- return this.traceStepManager.findStepOutBack(currentStep)
-}
-
-TraceManager.prototype.findStepOutForward = function (currentStep) {
- return this.traceStepManager.findStepOutForward(currentStep)
-}
-
TraceManager.prototype.findNextCall = function (currentStep) {
return this.traceStepManager.findNextCall(currentStep)
}
+TraceManager.prototype.findStepOut = function (currentStep) {
+ return this.traceStepManager.findStepOut(currentStep)
+}
+
// util
TraceManager.prototype.checkRequestedStep = function (stepIndex) {
if (!this.trace) {
@@ -291,4 +261,16 @@ TraceManager.prototype.checkRequestedStep = function (stepIndex) {
return undefined
}
+TraceManager.prototype.waterfall = function (calls, stepindex, cb) {
+ var ret = []
+ var retError = null
+ for (var call in calls) {
+ calls[call].apply(this, [stepindex, function (error, result) {
+ retError = error
+ ret.push({ error: error, value: result })
+ }])
+ }
+ cb(retError, ret)
+}
+
module.exports = TraceManager
diff --git a/remix-debug/src/trace/traceRetriever.js b/remix-debug/src/trace/traceRetriever.js
new file mode 100644
index 0000000000..07e757c0f0
--- /dev/null
+++ b/remix-debug/src/trace/traceRetriever.js
@@ -0,0 +1,19 @@
+'use strict'
+
+function TraceRetriever (options) {
+ this.web3 = options.web3
+}
+
+TraceRetriever.prototype.getTrace = function (txHash, callback) {
+ var options = {
+ disableStorage: true,
+ disableMemory: false,
+ disableStack: false,
+ fullStorage: false
+ }
+ this.web3.debug.traceTransaction(txHash, options, function (error, result) {
+ callback(error, result)
+ })
+}
+
+module.exports = TraceRetriever
diff --git a/remix-debug/src/trace/traceStepManager.js b/remix-debug/src/trace/traceStepManager.js
new file mode 100644
index 0000000000..5b4f50a625
--- /dev/null
+++ b/remix-debug/src/trace/traceStepManager.js
@@ -0,0 +1,58 @@
+'use strict'
+var remixLib = require('remix-lib')
+var traceHelper = remixLib.helpers.trace
+var util = remixLib.util
+
+function TraceStepManager (_traceAnalyser) {
+ this.traceAnalyser = _traceAnalyser
+}
+
+TraceStepManager.prototype.isCallInstruction = function (index) {
+ var state = this.traceAnalyser.trace[index]
+ return traceHelper.isCallInstruction(state) && !traceHelper.isCallToPrecompiledContract(index, this.traceAnalyser.trace)
+}
+
+TraceStepManager.prototype.isReturnInstruction = function (index) {
+ var state = this.traceAnalyser.trace[index]
+ return traceHelper.isReturnInstruction(state)
+}
+
+TraceStepManager.prototype.findStepOverBack = function (currentStep) {
+ if (this.isReturnInstruction(currentStep)) {
+ var call = util.findCall(currentStep, this.traceAnalyser.traceCache.callsTree.call)
+ return call.start > 0 ? call.start - 1 : 0
+ } else {
+ return currentStep > 0 ? currentStep - 1 : 0
+ }
+}
+
+TraceStepManager.prototype.findStepOverForward = function (currentStep) {
+ if (this.isCallInstruction(currentStep)) {
+ var call = util.findCall(currentStep + 1, this.traceAnalyser.traceCache.callsTree.call)
+ return call.return + 1 < this.traceAnalyser.trace.length ? call.return + 1 : this.traceAnalyser.trace.length - 1
+ } else {
+ return this.traceAnalyser.trace.length >= currentStep + 1 ? currentStep + 1 : currentStep
+ }
+}
+
+TraceStepManager.prototype.findNextCall = function (currentStep) {
+ var call = util.findCall(currentStep, this.traceAnalyser.traceCache.callsTree.call)
+ var subCalls = Object.keys(call.calls)
+ if (subCalls.length) {
+ var callStart = util.findLowerBound(currentStep, subCalls) + 1
+ if (subCalls.length > callStart) {
+ return subCalls[callStart] - 1
+ } else {
+ return currentStep
+ }
+ } else {
+ return currentStep
+ }
+}
+
+TraceStepManager.prototype.findStepOut = function (currentStep) {
+ var call = util.findCall(currentStep, this.traceAnalyser.traceCache.callsTree.call)
+ return call.return
+}
+
+module.exports = TraceStepManager
diff --git a/test/codeManager.js b/remix-debug/test/codeManager.js
similarity index 77%
rename from test/codeManager.js
rename to remix-debug/test/codeManager.js
index 2d9ceb9da3..a0709226de 100644
--- a/test/codeManager.js
+++ b/remix-debug/test/codeManager.js
@@ -1,10 +1,12 @@
'use strict'
var tape = require('tape')
-var Web3Providers = require('../src/web3Provider/web3Providers')
+var remixLib = require('remix-lib')
+var Web3Providers = remixLib.vm.Web3Providers
var TraceManager = require('../src/trace/traceManager')
var CodeManager = require('../src/code/codeManager')
var web3Test = require('./resources/testWeb3')
-var util = require('../src/helpers/global')
+
+let web3 = null
tape('CodeManager', function (t) {
var codeManager
@@ -16,12 +18,12 @@ tape('CodeManager', function (t) {
console.log(mes)
t.fail(mes)
} else {
- util.web3 = obj
- var traceManager = new TraceManager()
+ web3 = obj
+ var traceManager = new TraceManager({web3: web3})
codeManager = new CodeManager(traceManager)
- var contractCode = util.web3.eth.getCode('0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5')
+ var contractCode = web3.eth.getCode('0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5')
codeManager.codeResolver.cacheExecutingCode('0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5', contractCode) // so a call to web3 is not necessary
- var tx = util.web3.eth.getTransaction('0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd51')
+ var tx = web3.eth.getTransaction('0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd51')
traceManager.resolveTrace(tx, function (error, result) {
if (error) {
t.fail(' - traceManager.resolveTrace - failed ' + result)
@@ -40,7 +42,7 @@ function continueTesting (t, codeManager) {
t.test('CodeManager.resolveStep', function (st) {
st.plan(6)
- codeManager.register('indexChanged', this, function (index) {
+ codeManager.event.register('changed', this, function (code, address, index) {
if (index === undefined || index === null) {
st.fail(index)
} else {
@@ -48,8 +50,7 @@ function continueTesting (t, codeManager) {
}
})
- codeManager.register('codeChanged', this, function (code, address, index) {
- console.log(address + ' ' + index + ' ' + code)
+ codeManager.event.register('changed', this, function (code, address, index) {
if (!code) {
st.fail('no codes')
} else {
@@ -63,7 +64,7 @@ function continueTesting (t, codeManager) {
}
}
})
- var tx = util.web3.eth.getTransaction('0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd51')
+ var tx = web3.eth.getTransaction('0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd51')
codeManager.resolveStep(0, tx)
codeManager.resolveStep(70, tx)
})
diff --git a/remix-debug/test/decoder/contracts/byteStorage.js b/remix-debug/test/decoder/contracts/byteStorage.js
new file mode 100644
index 0000000000..dfd5e04fff
--- /dev/null
+++ b/remix-debug/test/decoder/contracts/byteStorage.js
@@ -0,0 +1,89 @@
+'use strict'
+
+module.exports = {
+ contract: `
+ contract byteStorage {
+ enum enum1 { e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, e20, e21, e22, e23, e24, e25, e26, e27, e28, e29, e30, e31, e32, e33, e34, e35, e36, e37, e38, e39, e40, e41, e42, e43, e44, e45, e46, e47, e48, e49, e50, e51, e52, e53, e54, e55, e56, e57, e58, e59, e60, e61, e62, e63, e64, e65, e66, e67, e68, e69, e70, e71, e72, e73, e74, e75, e76, e77, e78, e79, e80, e81, e82, e83, e84, e85, e86, e87, e88, e89, e90, e91, e92, e93, e94, e95, e96, e97, e98, e99, e100, e101, e102, e103, e104, e105, e106, e107, e108, e109, e110, e111, e112, e113, e114, e115, e116, e117, e118, e119, e120, e121, e122, e123, e124, e125, e126, e127, e128, e129, e130, e131, e132, e133, e134, e135, e136, e137, e138, e139, e140, e141, e142, e143, e144, e145, e146, e147, e148, e149, e150, e151, e152, e153, e154, e155, e156, e157, e158, e159, e160, e161, e162, e163, e164, e165, e166, e167, e168, e169, e170, e171, e172, e173, e174, e175, e176, e177, e178, e179, e180, e181, e182, e183, e184, e185, e186, e187, e188, e189, e190, e191, e192, e193, e194, e195, e196, e197, e198, e199, e200, e201, e202, e203, e204, e205, e206, e207, e208, e209, e210, e211, e212, e213, e214, e215, e216, e217, e218, e219, e220, e221, e222, e223, e224, e225, e226, e227, e228, e229, e230, e231, e232, e233, e234, e235, e236, e237, e238, e239, e240, e241, e242, e243, e244, e245, e246, e247, e248, e249, e250, e251, e252, e253, e254, e255, e256, e257, e258, e259, e260 }
+
+ bool b1 = false;
+ address a1 = 0xfe350f199f244ac9a79038d254400b632a633225;
+ bool b2 = true;
+ bytes dynb1 = "dynamicbytes";
+ byte stab = 0x1;
+ bytes1 stab1 = hex"12";
+ bytes2 stab2 = hex"1579";
+ bytes3 stab3 = hex"359356";
+ bytes4 stab4 = hex"2375";
+ bytes5 stab5 = hex"02357645";
+ bytes6 stab6 = hex"324435";
+ bytes7 stab7 = hex"004324";
+ bytes8 stab8 = hex"324554645765";
+ bytes9 stab9 = hex"03434543";
+ bytes10 stab10 = hex"04543543654657";
+ bytes11 stab11 = hex"54354654";
+ bytes12 stab12 = hex"03";
+ bytes13 stab13 = hex"03243242345435";
+ bytes14 stab14 = hex"32454354354353";
+ bytes15 stab15 = hex"032454434435";
+ bytes16 stab16 = hex"3245435444";
+ bytes17 stab17 = hex"032454343243243245";
+ bytes18 stab18 = hex"0324534325435435";
+ bytes19 stab19 = hex"0324543435435435";
+ bytes20 stab20 = hex"032454543543AB35";
+ bytes21 stab21 = hex"32454432423435";
+ bytes22 stab22 = hex"324543AEF5";
+ bytes23 stab23 = hex"3245435FFF";
+ bytes24 stab24 = hex"3245435F";
+ bytes25 stab25 = hex"3245435F";
+ bytes26 stab26 = hex"3245435F";
+ bytes27 stab27 = hex"03245FFFFFFF";
+ bytes28 stab28 = hex"03241235";
+ bytes29 stab29 = hex"0325213213";
+ bytes30 stab30 = hex"03245435232423";
+ bytes31 stab31 = hex"3245435123";
+ bytes32 stab32 = hex"324324423432543543AB";
+ enum1 enumDec = enum1.e240;
+ string str1 = 'short';
+ string str12 = 'шеллы';
+ string str2 = 'long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long';
+ }
+`,
+ storage: {
+ '0x0000000000000000000000000000000000000000000000000000000000000000': '0x0000000000000000000001fe350f199f244ac9a79038d254400b632a63322500',
+ '0x0000000000000000000000000000000000000000000000000000000000000001': '0x64796e616d696362797465730000000000000000000000000000000000000018',
+ '0x0000000000000000000000000000000000000000000000000000000000000002': '0x0000000043240000000032443500000002357645002375000035935615791201',
+ '0x0000000000000000000000000000000000000000000000000000000000000003': '0x0000000000045435436546570000000343454300000000003245546457650000',
+ '0x0000000000000000000000000000000000000000000000000000000000000004': '0x0000000000000000000300000000000000000000005435465400000000000000',
+ '0x0000000000000000000000000000000000000000000000000000000000000005': '0x0000000000324543543543530000000000000003243242345435000000000000',
+ '0x0000000000000000000000000000000000000000000000000000000000000006': '0x0032454354440000000000000000000000032454434435000000000000000000',
+ '0x0000000000000000000000000000000000000000000000000000000000000007': '0x0000000000000000000000000000000324543432432432450000000000000000',
+ '0x0000000000000000000000000000000000000000000000000000000000000008': '0x0000000000000000000000000000032453432543543500000000000000000000',
+ '0x0000000000000000000000000000000000000000000000000000000000000009': '0x0000000000000000000000000003245434354354350000000000000000000000',
+ '0x000000000000000000000000000000000000000000000000000000000000000a': '0x000000000000000000000000032454543543ab35000000000000000000000000',
+ '0x000000000000000000000000000000000000000000000000000000000000000b': '0x0000000000000000000000324544324234350000000000000000000000000000',
+ '0x000000000000000000000000000000000000000000000000000000000000000c': '0x00000000000000000000324543aef50000000000000000000000000000000000',
+ '0x000000000000000000000000000000000000000000000000000000000000000d': '0x0000000000000000003245435fff000000000000000000000000000000000000',
+ '0x000000000000000000000000000000000000000000000000000000000000000e': '0x00000000000000003245435f0000000000000000000000000000000000000000',
+ '0x000000000000000000000000000000000000000000000000000000000000000f': '0x000000000000003245435f000000000000000000000000000000000000000000',
+ '0x0000000000000000000000000000000000000000000000000000000000000010': '0x0000000000003245435f00000000000000000000000000000000000000000000',
+ '0x0000000000000000000000000000000000000000000000000000000000000011': '0x000000000003245fffffff000000000000000000000000000000000000000000',
+ '0x0000000000000000000000000000000000000000000000000000000000000012': '0x0000000003241235000000000000000000000000000000000000000000000000',
+ '0x0000000000000000000000000000000000000000000000000000000000000013': '0x0000000325213213000000000000000000000000000000000000000000000000',
+ '0x0000000000000000000000000000000000000000000000000000000000000014': '0x0000032454352324230000000000000000000000000000000000000000000000',
+ '0x0000000000000000000000000000000000000000000000000000000000000015': '0x0032454351230000000000000000000000000000000000000000000000000000',
+ '0x0000000000000000000000000000000000000000000000000000000000000016': '0x324324423432543543ab00000000000000000000000000000000000000000000',
+ '0x0000000000000000000000000000000000000000000000000000000000000017': '0x00000000000000000000000000000000000000000000000000000000000000f0',
+ '0x0000000000000000000000000000000000000000000000000000000000000018': '0x73686f727400000000000000000000000000000000000000000000000000000a',
+ '0x0000000000000000000000000000000000000000000000000000000000000019': '0xd188d0b5d0bbd0bbd18b00000000000000000000000000000000000000000014',
+ '0x000000000000000000000000000000000000000000000000000000000000001a': '0x000000000000000000000000000000000000000000000000000000000000023d',
+ '0x057c384a7d1c54f3a1b2e5e67b2617b8224fdfd1ea7234eea573a6ff665ff63e': '0x6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f',
+ '0x057c384a7d1c54f3a1b2e5e67b2617b8224fdfd1ea7234eea573a6ff665ff63f': '0x6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e67',
+ '0x057c384a7d1c54f3a1b2e5e67b2617b8224fdfd1ea7234eea573a6ff665ff640': '0x5f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f',
+ '0x057c384a7d1c54f3a1b2e5e67b2617b8224fdfd1ea7234eea573a6ff665ff641': '0x6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f',
+ '0x057c384a7d1c54f3a1b2e5e67b2617b8224fdfd1ea7234eea573a6ff665ff642': '0x6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e67',
+ '0x057c384a7d1c54f3a1b2e5e67b2617b8224fdfd1ea7234eea573a6ff665ff643': '0x5f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f',
+ '0x057c384a7d1c54f3a1b2e5e67b2617b8224fdfd1ea7234eea573a6ff665ff644': '0x6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f',
+ '0x057c384a7d1c54f3a1b2e5e67b2617b8224fdfd1ea7234eea573a6ff665ff645': '0x6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e67',
+ '0x057c384a7d1c54f3a1b2e5e67b2617b8224fdfd1ea7234eea573a6ff665ff646': '0x5f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e675f5f6c6f6e670000'
+ }
+}
diff --git a/remix-debug/test/decoder/contracts/intLocal.js b/remix-debug/test/decoder/contracts/intLocal.js
new file mode 100644
index 0000000000..1bfaf7c5ec
--- /dev/null
+++ b/remix-debug/test/decoder/contracts/intLocal.js
@@ -0,0 +1,42 @@
+'use strict'
+
+module.exports = {
+ contract: `
+contract proxy {
+ struct testStruct {
+ int one;
+ uint two;
+ }
+}
+contract intLocal {
+ function intLocal () {
+ proxy.testStruct memory p;
+ uint8 ui8 = 130;
+ uint16 ui16 = 456;
+ uint32 ui32 = 4356;
+ uint64 ui64 = 3543543543;
+ uint128 ui128 = 234567;
+ uint256 ui256 = 115792089237316195423570985008687907853269984665640564039457584007880697216513;
+ uint ui = 123545666;
+ int8 i8 = -45;
+ int16 i16 = -1234;
+ int32 i32 = 3455;
+ int64 i64 = -35566;
+ int128 i128 = -444444;
+ int256 i256 = 3434343;
+ int i = -32432423423;
+ int32 ishrink = 2;
+ level11();
+ level12();
+ level11();
+ }
+
+ function level11() {
+ uint8 ui8 = 123;
+ level12();
+ }
+ function level12() {
+ uint8 ui81 = 12;
+ }
+ }
+`}
diff --git a/remix-debug/test/decoder/contracts/intStorage.js b/remix-debug/test/decoder/contracts/intStorage.js
new file mode 100644
index 0000000000..ec2ed7719e
--- /dev/null
+++ b/remix-debug/test/decoder/contracts/intStorage.js
@@ -0,0 +1,32 @@
+'use strict'
+
+module.exports = {
+ contract: `
+ contract intStorage {
+ uint8 ui8 = 130;
+ uint16 ui16 = 456;
+ uint32 ui32 = 4356;
+ uint64 ui64 = 3543543543;
+ uint128 ui128 = 234567;
+ uint256 public ui256 = 115792089237316195423570985008687907853269984665640564039457584007880697216513;
+ uint ui = 123545666;
+ int8 i8 = -45;
+ int16 i16 = -1234;
+ int32 i32 = 3455;
+ int64 i64 = -35566;
+ int128 i128 = -444444;
+ int256 i256 = 3434343;
+ int i = -32432423423;
+ int32 ishrink = 2;
+ }
+`,
+ fullStorage: {
+ '0x0000000000000000000000000000000000000000000000000000000000000000': '0x000000000000000000000000000003944700000000d3362ef70000110401c882',
+ '0x0000000000000000000000000000000000000000000000000000000000000001': '0xfffffffffffffffffffffffffffffffffffffffffffffffffffffff872e07e01',
+ '0x0000000000000000000000000000000000000000000000000000000000000002': '0x00000000000000000000000000000000000000000000000000000000075d2842',
+ '0x0000000000000000000000000000000000000000000000000000000000000003': '0x00fffffffffffffffffffffffffff937e4ffffffffffff751200000d7ffb2ed3',
+ '0x0000000000000000000000000000000000000000000000000000000000000004': '0x0000000000000000000000000000000000000000000000000000000000346767',
+ '0x0000000000000000000000000000000000000000000000000000000000000005': '0xfffffffffffffffffffffffffffffffffffffffffffffffffffffff872e07e01',
+ '0x0000000000000000000000000000000000000000000000000000000000000006': '0x0000000000000000000000000000000000000000000000000000000000000002'
+ }
+}
diff --git a/remix-debug/test/decoder/contracts/mappingStorage.js b/remix-debug/test/decoder/contracts/mappingStorage.js
new file mode 100644
index 0000000000..722c36a458
--- /dev/null
+++ b/remix-debug/test/decoder/contracts/mappingStorage.js
@@ -0,0 +1,16 @@
+module.exports = {
+ contract: `
+ pragma solidity ^0.4.19;
+
+contract SimpleMappingState {
+ uint _num;
+ mapping(string => uint) _iBreakSolidityState;
+ mapping(uint => uint) _iBreakSolidityStateInt;
+ function updateNum(uint num, string str) public {
+ _num = num;
+ _iBreakSolidityState[str] = num;
+ _iBreakSolidityStateInt[num] = num;
+ }
+}
+ `
+}
diff --git a/remix-debug/test/decoder/contracts/miscContracts.js b/remix-debug/test/decoder/contracts/miscContracts.js
new file mode 100644
index 0000000000..7e8f9214be
--- /dev/null
+++ b/remix-debug/test/decoder/contracts/miscContracts.js
@@ -0,0 +1,80 @@
+'use strict'
+module.exports = `
+ contract baseContract {
+ uint8 u;
+ }
+
+ contract contractUint is baseContract {
+ uint256 ui;
+ uint ui1;
+ bytes16 b;
+ }
+
+ contract contractStructAndArray {
+ struct structDef {
+ uint8 ui;
+ string str;
+ }
+ structDef structDec;
+ structDef[3] array;
+ bytes12[4] bytesArray;
+ }
+
+ contract contractArray {
+ uint32[5] i32st;
+ int8[] i8dyn;
+ int16[][3][][4] i16dyn;
+ }
+
+ contract contractEnum {
+ enum enumDef {item0,item1,item2,item3,item4,item5,item6,item7,item8,item9,item10,item11,item12,item13,item14,item15,item16,item17,item18,item19,item20,item21,item22,item23,item24,item25,item26,item27,item28,item29,item30,item31,item32,item33,item34,item35,item36,item37,item38,item39,item40,item41,item42,item43,item44,item45,item46,item47,item48,item49,item50,item51,item52,item53,item54,item55,item56,item57,item58,item59,item60,item61,item62,item63,item64,item65,item66,item67,item68,item69,item70,item71,item72,item73,item74,item75,item76,item77,item78,item79,item80,item81,item82,item83,item84,item85,item86,item87,item88,item89,item90,item91,item92,item93,item94,item95,item96,item97,item98,item99,item100,item101,item102,item103,item104,item105,item106,item107,item108,item109,item110,item111,item112,item113,item114,item115,item116,item117,item118,item119,item120,item121,item122,item123,item124,item125,item126,item127,item128,item129,item130,item131,item132,item133,item134,item135,item136,item137,item138,item139,item140,item141,item142,item143,item144,item145,item146,item147,item148,item149,item150,item151,item152,item153,item154,item155,item156,item157,item158,item159,item160,item161,item162,item163,item164,item165,item166,item167,item168,item169,item170,item171,item172,item173,item174,item175,item176,item177,item178,item179,item180,item181,item182,item183,item184,item185,item186,item187,item188,item189,item190,item191,item192,item193,item194,item195,item196,item197,item198,item199,item200,item201,item202,item203,item204,item205,item206,item207,item208,item209,item210,item211,item212,item213,item214,item215,item216,item217,item218,item219,item220,item221,item222,item223,item224,item225,item226,item227,item228,item229,item230,item231,item232,item233,item234,item235,item236,item237,item238,item239,item240,item241,item242,item243,item244,item245,item246,item247,item248,item249,item250,item251,item252,item253,item254,item255,item256,item257,item258,item259,item260,item261,item262,item263,item264,item265,item266,item267,item268,item269,item270,item271,item272,item273,item274,item275,item276,item277,item278,item279,item280,item281,item282,item283,item284,item285,item286,item287,item288,item289,item290,item291,item292,item293,item294,item295,item296,item297,item298,item299,item100000000}
+ enumDef enum1;
+ }
+
+ contract contractSmallVariable {
+ int8 i8;
+ uint8 iu8;
+ uint16 iu18;
+ int32 i32;
+ uint ui32;
+ int16 i16;
+ }
+
+ contract testSimpleStorage1 {
+ uint32 uibase1;
+ }
+
+ contract testSimpleStorage is testSimpleStorage1 {
+ uint ui1;
+ uint ui2;
+ uint[1] ui3;
+ uint[][1][4] ui4;
+
+ int16 i16;
+
+ struct structDef {
+ uint ui;
+ string str;
+ }
+
+ structDef structDec;
+
+ structDef[3] arrayStructDec;
+
+ int32 i32;
+ int16 i16_2;
+
+ enum enumDef {
+ first,
+ second,
+ third
+ }
+
+ enumDef enumDec;
+ bool boolean;
+
+ uint[][2][][3] ui5;
+
+ string _string;
+ }
+`
diff --git a/remix-debug/test/decoder/contracts/miscLocal.js b/remix-debug/test/decoder/contracts/miscLocal.js
new file mode 100644
index 0000000000..c6a37b6e09
--- /dev/null
+++ b/remix-debug/test/decoder/contracts/miscLocal.js
@@ -0,0 +1,39 @@
+'use strict'
+
+module.exports = {
+ contract: `
+contract miscLocal {
+ enum enumDef {
+ one,
+ two,
+ three,
+ four
+ }
+ function miscLocal () {
+ bool boolFalse = false;
+ bool boolTrue = true;
+ enumDef testEnum;
+ testEnum = enumDef.three;
+ address sender = msg.sender;
+ byte _bytes1 = hex"99";
+ bytes1 __bytes1 = hex"99";
+ bytes2 __bytes2 = hex"99AB";
+ bytes4 __bytes4 = hex"99FA";
+ bytes6 __bytes6 = hex"99";
+ bytes7 __bytes7 = hex"993567";
+ bytes8 __bytes8 = hex"99ABD417";
+ bytes9 __bytes9 = hex"99156744AF";
+ bytes13 __bytes13 = hex"991234234253";
+ bytes16 __bytes16 = hex"99AFAD234324";
+ bytes24 __bytes24 = hex"99AFAD234324";
+ bytes32 __bytes32 = hex"9999ABD41799ABD417";
+ }
+ }
+
+ contract miscLocal2 {
+ function miscLocal2 () {
+ bytes memory dynbytes = "dynamicbytes";
+ string memory smallstring = "test_test_test";
+ }
+ }
+`}
diff --git a/remix-debug/test/decoder/contracts/simpleContract.js b/remix-debug/test/decoder/contracts/simpleContract.js
new file mode 100644
index 0000000000..bffa2bfe88
--- /dev/null
+++ b/remix-debug/test/decoder/contracts/simpleContract.js
@@ -0,0 +1,26 @@
+'use strict'
+module.exports = `
+ contract simpleContract {
+ struct structDef {
+ uint8 ui;
+ string str;
+ }
+ enum enumDef {
+ first,
+ second,
+ third
+ }
+ structDef structDec;
+ structDef[3] array;
+ enumDef enumDec;
+ }
+
+ contract test1 {
+ struct str {
+ }
+ }
+
+ contract test2 {
+ test1.str a;
+ }
+`
diff --git a/remix-debug/test/decoder/contracts/structArrayLocal.js b/remix-debug/test/decoder/contracts/structArrayLocal.js
new file mode 100644
index 0000000000..35bf48ea69
--- /dev/null
+++ b/remix-debug/test/decoder/contracts/structArrayLocal.js
@@ -0,0 +1,84 @@
+'use strict'
+
+module.exports = {
+ contract: `
+contract structArrayLocal {
+ struct teststruct {
+ string a;
+ int b;
+ string c;
+ int d;
+ bool e;
+ }
+
+ enum enumdef
+ {
+ one,
+ two,
+ three
+ }
+
+ struct teststructArray {
+ string[] a;
+ int8[3] b;
+ enumdef c;
+ }
+
+ function structArrayLocal () {
+ bytes memory bytesSimple = "test_super";
+ teststruct memory e;
+ e.a = "test";
+ e.b = 5;
+ string memory f = "test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_";
+ e.c = "test_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_test";
+ e.d = 3;
+ e.e = true;
+
+ int[5] memory simpleArray;
+ simpleArray[0] = 45;
+ simpleArray[1] = 324324;
+ simpleArray[2] = -333;
+ simpleArray[3] = 5656;
+ simpleArray[4] = -1111;
+
+ string[3] memory stringArray;
+ stringArray[0] = "long_one_long_one_long_one_long_one_long_one_long_one_long_one_long_one_long_one_long_one_long_one_long_one_long_one_long_one_long_one_";
+ stringArray[1] = "two";
+ stringArray[2] = "three";
+
+ int[][3] memory dynArray;
+ dynArray[0] = new int[](1);
+ dynArray[1] = new int[](2);
+ dynArray[2] = new int[](3);
+ dynArray[0][0] = 3423423532;
+ dynArray[1][0] = -342343323532;
+ dynArray[1][1] = 23432;
+ dynArray[2][0] = -432432;
+ dynArray[2][1] = 3423423532;
+ dynArray[2][2] = -432432;
+
+ teststruct[3] memory structArray;
+ structArray[0] = e;
+
+ structArray[1].a = "item1 a";
+ structArray[1].b = 20;
+ structArray[1].c = "item1 c";
+ structArray[1].d = -45;
+ structArray[1].e = false;
+
+ structArray[2].a = "item2 a";
+ structArray[2].b = 200;
+ structArray[2].c = "item2 c";
+ structArray[2].d = -450;
+ structArray[2].e = true;
+
+ teststructArray memory arrayStruct;
+ arrayStruct.a = new string[](1);
+ arrayStruct.a[0] = "string";
+ arrayStruct.b[0] = 34;
+ arrayStruct.b[1] = -23;
+ arrayStruct.b[2] = -3;
+ arrayStruct.c = enumdef.three;
+ }
+}
+`}
diff --git a/remix-debug/test/decoder/contracts/structArrayStorage.js b/remix-debug/test/decoder/contracts/structArrayStorage.js
new file mode 100644
index 0000000000..7f542733f6
--- /dev/null
+++ b/remix-debug/test/decoder/contracts/structArrayStorage.js
@@ -0,0 +1,204 @@
+'use strict'
+
+module.exports = {
+ contract: `contract structArrayStorage {
+ struct intStruct {
+ int8 i8;
+ int16 i16;
+ uint32 ui32;
+ int i256;
+ uint16 ui16;
+ int32 i32;
+ }
+ intStruct intStructDec;
+
+ int64[7] i5;
+
+ int64[] idyn5;
+
+ int32[][4] dyn1;
+
+ int32[][4][] dyn2;
+
+ struct simpleStruct {
+ int8 i8;
+ string str;
+ }
+ simpleStruct[][3] arrayStruct;
+ function structArrayStorage () {
+ intStructDec.i8 = 32;
+ intStructDec.i16 = -54;
+ intStructDec.ui32 = 128;
+ intStructDec.i256 = -1243565465756;
+ intStructDec.ui16 = 34556;
+ intStructDec.i32 = -345446546;
+
+ i5[0] = -2134;
+ i5[1] = 345;
+ i5[2] = -3246;
+ i5[3] = 4357;
+ i5[4] = 324;
+ i5[5] = -2344;
+ i5[6] = 3254;
+
+ idyn5.length = 9;
+ idyn5[0] = -2134;
+ idyn5[1] = 345;
+ idyn5[2] = -3246;
+ idyn5[3] = 4357;
+ idyn5[4] = 324;
+ idyn5[5] = -2344;
+ idyn5[6] = 3254;
+ idyn5[7] = -254;
+ idyn5[8] = -2354;
+
+ dyn1[0].length = 1;
+ dyn1[1].length = 3;
+ dyn1[2].length = 10;
+ dyn1[3].length = 2;
+
+ dyn1[0][0] = 3;
+ dyn1[1][0] = 12;
+ dyn1[1][1] = -12;
+ dyn1[1][2] = -1234;
+
+ dyn1[2][0] = 1;
+ dyn1[2][1] = 12;
+ dyn1[2][2] = 1235;
+ dyn1[2][3] = -12;
+ dyn1[2][4] = -123456;
+ dyn1[2][5] = -23435435;
+ dyn1[2][6] = 543543;
+ dyn1[2][7] = 2;
+ dyn1[2][8] = -1;
+ dyn1[2][9] = 232432;
+ dyn1[3][0] = 232432;
+ dyn1[3][1] = 232432;
+
+ dyn2.length = 2;
+ dyn2[0][0].length = 3;
+ dyn2[0][1].length = 3;
+ dyn2[0][2].length = 3;
+ dyn2[0][3].length = 3;
+ dyn2[1][0].length = 3;
+ dyn2[1][1].length = 3;
+ dyn2[1][2].length = 3;
+ dyn2[1][3].length = 3;
+
+ dyn2[0][0][0] = 23;
+ dyn2[0][0][1] = -23;
+ dyn2[0][0][2] = -28;
+
+ dyn2[0][1][0] = 23;
+ dyn2[0][1][1] = -23;
+ dyn2[0][1][2] = -28;
+
+ dyn2[0][2][0] = 23;
+ dyn2[0][2][1] = -23;
+ dyn2[0][2][2] = -28;
+
+ dyn2[0][3][0] = 23;
+ dyn2[0][3][1] = -23;
+ dyn2[0][3][2] = -28;
+
+ dyn2[1][0][0] = 23;
+ dyn2[1][0][1] = -23;
+ dyn2[1][0][2] = -28;
+
+ dyn2[1][1][0] = 23;
+ dyn2[1][1][1] = -23;
+ dyn2[1][1][2] = -28;
+
+ dyn2[1][2][0] = 23;
+ dyn2[1][2][1] = -23;
+ dyn2[1][2][2] = -28;
+
+ dyn2[1][3][0] = 23;
+ dyn2[1][3][1] = -23;
+ dyn2[1][3][2] = -28;
+
+
+ arrayStruct[0].length = 2;
+ arrayStruct[1].length = 1;
+ arrayStruct[2].length = 3;
+
+ arrayStruct[0][0].i8 = 34;
+ arrayStruct[0][0].str = 'test_str_short';
+ arrayStruct[0][1].i8 = -123;
+ arrayStruct[0][1].str = 'test_str_long test_str_lo ngtest_str_longtest_str_ longtest_str_longtest_ str_longtest_str_l ongtest_str_long';
+
+ arrayStruct[1][0].i8 = 50;
+ arrayStruct[1][0].str = 'test_str_short';
+
+ arrayStruct[2][0].i8 = 60;
+ arrayStruct[2][0].str = 'test_str_short';
+ arrayStruct[2][1].i8 = 84;
+ arrayStruct[2][1].str = 'test_str_long test_str_lo ngtest_str_longtest_str_ longtest_str_longtest_ str_longtest_str_l ongtest_str_long';
+ arrayStruct[2][2].i8 = -34;
+ arrayStruct[2][2].str = 'test_str_short';
+
+
+ }
+}
+`,
+ storage: {
+ '0x0000000000000000000000000000000000000000000000000000000000000000': '0x0000000000000000000000000000000000000000000000000000000080ffca20',
+ '0x0000000000000000000000000000000000000000000000000000000000000001': '0xfffffffffffffffffffffffffffffffffffffffffffffffffffffede75b8df64',
+ '0x0000000000000000000000000000000000000000000000000000000000000002': '0x0000000000000000000000000000000000000000000000000000eb68e76e86fc',
+ '0x0000000000000000000000000000000000000000000000000000000000000003': '0x0000000000001105fffffffffffff3520000000000000159fffffffffffff7aa',
+ '0x0000000000000000000000000000000000000000000000000000000000000004': '0x00000000000000000000000000000cb6fffffffffffff6d80000000000000144',
+ '0x0000000000000000000000000000000000000000000000000000000000000005': '0x0000000000000000000000000000000000000000000000000000000000000009',
+ '0x036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0': '0x0000000000001105fffffffffffff3520000000000000159fffffffffffff7aa',
+ '0x036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db1': '0xffffffffffffff020000000000000cb6fffffffffffff6d80000000000000144',
+ '0x036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db2': '0x000000000000000000000000000000000000000000000000fffffffffffff6ce',
+ '0x0000000000000000000000000000000000000000000000000000000000000006': '0x0000000000000000000000000000000000000000000000000000000000000001',
+ '0x0000000000000000000000000000000000000000000000000000000000000007': '0x0000000000000000000000000000000000000000000000000000000000000003',
+ '0x0000000000000000000000000000000000000000000000000000000000000008': '0x000000000000000000000000000000000000000000000000000000000000000a',
+ '0x0000000000000000000000000000000000000000000000000000000000000009': '0x0000000000000000000000000000000000000000000000000000000000000002',
+ '0xf652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f': '0x0000000000000000000000000000000000000000000000000000000000000003',
+ '0xa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c688': '0x0000000000000000000000000000000000000000fffffb2efffffff40000000c',
+ '0xf3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee3': '0x0000000200084b37fe9a6755fffe1dc0fffffff4000004d30000000c00000001',
+ '0xf3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee4': '0x00000000000000000000000000000000000000000000000000038bf0ffffffff',
+ '0x6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af': '0x00000000000000000000000000000000000000000000000000038bf000038bf0',
+ '0x000000000000000000000000000000000000000000000000000000000000000a': '0x0000000000000000000000000000000000000000000000000000000000000002',
+ '0xc65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2a8': '0x0000000000000000000000000000000000000000000000000000000000000003',
+ '0xc65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2a9': '0x0000000000000000000000000000000000000000000000000000000000000003',
+ '0xc65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2aa': '0x0000000000000000000000000000000000000000000000000000000000000003',
+ '0xc65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2ab': '0x0000000000000000000000000000000000000000000000000000000000000003',
+ '0xc65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2ac': '0x0000000000000000000000000000000000000000000000000000000000000003',
+ '0xc65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2ad': '0x0000000000000000000000000000000000000000000000000000000000000003',
+ '0xc65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2ae': '0x0000000000000000000000000000000000000000000000000000000000000003',
+ '0xc65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2af': '0x0000000000000000000000000000000000000000000000000000000000000003',
+ '0x410c2796757c1866e144712b649ab035b22d7295530f125d2b7bc17fa7b793b5': '0x0000000000000000000000000000000000000000ffffffe4ffffffe900000017',
+ '0x06b493c1ca289c5326ef56c162cd187bf96c737c2c9bbda318cc345be15042af': '0x0000000000000000000000000000000000000000ffffffe4ffffffe900000017',
+ '0x7a0b543a77c72a2154fae01417d93ab4a7f07c9a6bbce5febfeb9904a41b7914': '0x0000000000000000000000000000000000000000ffffffe4ffffffe900000017',
+ '0x5370eae143cc2f6260640bd734b0cdaf587bbcfc81362df39d56d5a29a7e663b': '0x0000000000000000000000000000000000000000ffffffe4ffffffe900000017',
+ '0xd5211e5652076f058928f5b24e1816690291c298b337ea927f8d0f3aabb8a05a': '0x0000000000000000000000000000000000000000ffffffe4ffffffe900000017',
+ '0xf232dee5d9edbb879fab95c81a3867fe42b8d79b05e9c99336c5297487f94e8d': '0x0000000000000000000000000000000000000000ffffffe4ffffffe900000017',
+ '0x8a82e6d20ae2c2a82dd8e575dac6354ce964fd35e3d1cdb79bb1757c6a7675b6': '0x0000000000000000000000000000000000000000ffffffe4ffffffe900000017',
+ '0xa9e6724ab7d0ccf2de69222bc5703c9df2049038736e6d57f437315272b76a3a': '0x0000000000000000000000000000000000000000ffffffe4ffffffe900000017',
+ '0x000000000000000000000000000000000000000000000000000000000000000b': '0x0000000000000000000000000000000000000000000000000000000000000002',
+ '0x000000000000000000000000000000000000000000000000000000000000000c': '0x0000000000000000000000000000000000000000000000000000000000000001',
+ '0x000000000000000000000000000000000000000000000000000000000000000d': '0x0000000000000000000000000000000000000000000000000000000000000003',
+ '0x0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db9': '0x0000000000000000000000000000000000000000000000000000000000000022',
+ '0x0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01dba': '0x746573745f7374725f73686f727400000000000000000000000000000000001c',
+ '0x0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01dbb': '0x0000000000000000000000000000000000000000000000000000000000000085',
+ '0x0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01dbc': '0x00000000000000000000000000000000000000000000000000000000000000db',
+ '0x40abea1508c7557b93b3e219e777ce8530b60f9f8452ef1c627dbc62b53708fc': '0x746573745f7374725f6c6f6e6720746573745f7374725f6c6f206e6774657374',
+ '0x40abea1508c7557b93b3e219e777ce8530b60f9f8452ef1c627dbc62b53708fd': '0x5f7374725f6c6f6e67746573745f7374725f206c6f6e67746573745f7374725f',
+ '0x40abea1508c7557b93b3e219e777ce8530b60f9f8452ef1c627dbc62b53708fe': '0x6c6f6e67746573745f207374725f6c6f6e67746573745f7374725f6c206f6e67',
+ '0x40abea1508c7557b93b3e219e777ce8530b60f9f8452ef1c627dbc62b53708ff': '0x746573745f7374725f6c6f6e6700000000000000000000000000000000000000',
+ '0xdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c7': '0x0000000000000000000000000000000000000000000000000000000000000032',
+ '0xdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c8': '0x746573745f7374725f73686f727400000000000000000000000000000000001c',
+ '0xd7b6990105719101dabeb77144f2a3385c8033acd3af97e9423a695e81ad1eb5': '0x000000000000000000000000000000000000000000000000000000000000003c',
+ '0xd7b6990105719101dabeb77144f2a3385c8033acd3af97e9423a695e81ad1eb6': '0x746573745f7374725f73686f727400000000000000000000000000000000001c',
+ '0xd7b6990105719101dabeb77144f2a3385c8033acd3af97e9423a695e81ad1eb7': '0x0000000000000000000000000000000000000000000000000000000000000054',
+ '0xd7b6990105719101dabeb77144f2a3385c8033acd3af97e9423a695e81ad1eb8': '0x00000000000000000000000000000000000000000000000000000000000000db',
+ '0x87466a1ae97409dd9d9cd9368751b439509d8b3f8fc2bb47a4264e5d6fd4d324': '0x746573745f7374725f6c6f6e6720746573745f7374725f6c6f206e6774657374',
+ '0x87466a1ae97409dd9d9cd9368751b439509d8b3f8fc2bb47a4264e5d6fd4d325': '0x5f7374725f6c6f6e67746573745f7374725f206c6f6e67746573745f7374725f',
+ '0x87466a1ae97409dd9d9cd9368751b439509d8b3f8fc2bb47a4264e5d6fd4d326': '0x6c6f6e67746573745f207374725f6c6f6e67746573745f7374725f6c206f6e67',
+ '0x87466a1ae97409dd9d9cd9368751b439509d8b3f8fc2bb47a4264e5d6fd4d327': '0x746573745f7374725f6c6f6e6700000000000000000000000000000000000000',
+ '0xd7b6990105719101dabeb77144f2a3385c8033acd3af97e9423a695e81ad1eb9': '0x00000000000000000000000000000000000000000000000000000000000000de',
+ '0xd7b6990105719101dabeb77144f2a3385c8033acd3af97e9423a695e81ad1eba': '0x746573745f7374725f73686f727400000000000000000000000000000000001c'
+ }
+}
diff --git a/remix-debug/test/decoder/decodeInfo.js b/remix-debug/test/decoder/decodeInfo.js
new file mode 100644
index 0000000000..4e100f2b79
--- /dev/null
+++ b/remix-debug/test/decoder/decodeInfo.js
@@ -0,0 +1,97 @@
+'use strict'
+var tape = require('tape')
+var compiler = require('solc')
+var astHelper = require('../../src/decoder/astHelper')
+var decodeInfo = require('../../src/decoder/decodeInfo')
+var stateDecoder = require('../../src/decoder/stateDecoder')
+var contracts = require('./contracts/miscContracts')
+var simplecontracts = require('./contracts/simpleContract')
+var remixLib = require('remix-lib')
+var compilerInput = remixLib.helpers.compiler.compilerInput
+var util = require('../../src/decoder/types/util')
+
+tape('solidity', function (t) {
+ t.test('astHelper, decodeInfo', function (st) {
+ var output = compiler.compileStandardWrapper(compilerInput(contracts))
+ output = JSON.parse(output)
+
+ var state = astHelper.extractStateDefinitions('test.sol:contractUint', output.sources)
+ var states = astHelper.extractStatesDefinitions(output.sources)
+ var stateDef = state.stateDefinitions
+ var parsedType = decodeInfo.parseType(stateDef[0].attributes.type, states, 'contractUint', util.extractLocationFromAstVariable(stateDef[0]))
+ checkDecodeInfo(st, parsedType, 1, 1, 'uint8')
+ parsedType = decodeInfo.parseType(stateDef[2].attributes.type, states, 'contractUint', util.extractLocationFromAstVariable(stateDef[2]))
+ checkDecodeInfo(st, parsedType, 1, 32, 'uint256')
+ parsedType = decodeInfo.parseType(stateDef[3].attributes.type, states, 'contractUint', util.extractLocationFromAstVariable(stateDef[3]))
+ checkDecodeInfo(st, parsedType, 1, 32, 'uint256')
+ parsedType = decodeInfo.parseType(stateDef[4].attributes.type, states, 'contractUint', util.extractLocationFromAstVariable(stateDef[4]))
+ checkDecodeInfo(st, parsedType, 1, 16, 'bytes16')
+
+ state = astHelper.extractStateDefinitions('test.sol:contractStructAndArray', output.sources)
+ stateDef = state.stateDefinitions
+ parsedType = decodeInfo.parseType(stateDef[1].attributes.type, states, 'contractStructAndArray', util.extractLocationFromAstVariable(stateDef[1]))
+ checkDecodeInfo(st, parsedType, 2, 32, 'struct contractStructAndArray.structDef')
+ parsedType = decodeInfo.parseType(stateDef[2].attributes.type, states, 'contractStructAndArray', util.extractLocationFromAstVariable(stateDef[2]))
+ checkDecodeInfo(st, parsedType, 6, 32, 'struct contractStructAndArray.structDef[3]')
+ parsedType = decodeInfo.parseType(stateDef[3].attributes.type, states, 'contractStructAndArray', util.extractLocationFromAstVariable(stateDef[3]))
+ checkDecodeInfo(st, parsedType, 2, 32, 'bytes12[4]')
+
+ state = astHelper.extractStateDefinitions('test.sol:contractArray', output.sources)
+ stateDef = state.stateDefinitions
+ parsedType = decodeInfo.parseType(stateDef[0].attributes.type, states, 'contractArray', util.extractLocationFromAstVariable(stateDef[0]))
+ checkDecodeInfo(st, parsedType, 1, 32, 'uint32[5]')
+ parsedType = decodeInfo.parseType(stateDef[1].attributes.type, states, 'contractArray', util.extractLocationFromAstVariable(stateDef[1]))
+ checkDecodeInfo(st, parsedType, 1, 32, 'int8[]')
+ parsedType = decodeInfo.parseType(stateDef[2].attributes.type, states, 'contractArray', util.extractLocationFromAstVariable(stateDef[2]))
+ checkDecodeInfo(st, parsedType, 4, 32, 'int16[][3][][4]')
+
+ state = astHelper.extractStateDefinitions('test.sol:contractEnum', output.sources)
+ stateDef = state.stateDefinitions
+ parsedType = decodeInfo.parseType(stateDef[1].attributes.type, states, 'contractEnum')
+ checkDecodeInfo(st, parsedType, 1, 2, 'enum')
+
+ state = astHelper.extractStateDefinitions('test.sol:contractSmallVariable', output.sources)
+ stateDef = state.stateDefinitions
+ parsedType = decodeInfo.parseType(stateDef[0].attributes.type, states, 'contractSmallVariable', util.extractLocationFromAstVariable(stateDef[0]))
+ checkDecodeInfo(st, parsedType, 1, 1, 'int8')
+ parsedType = decodeInfo.parseType(stateDef[1].attributes.type, states, 'contractSmallVariable', util.extractLocationFromAstVariable(stateDef[1]))
+ checkDecodeInfo(st, parsedType, 1, 1, 'uint8')
+ parsedType = decodeInfo.parseType(stateDef[2].attributes.type, states, 'contractSmallVariable', util.extractLocationFromAstVariable(stateDef[2]))
+ checkDecodeInfo(st, parsedType, 1, 2, 'uint16')
+ parsedType = decodeInfo.parseType(stateDef[3].attributes.type, states, 'contractSmallVariable', util.extractLocationFromAstVariable(stateDef[3]))
+ checkDecodeInfo(st, parsedType, 1, 4, 'int32')
+ parsedType = decodeInfo.parseType(stateDef[4].attributes.type, states, 'contractSmallVariable', util.extractLocationFromAstVariable(stateDef[4]))
+ checkDecodeInfo(st, parsedType, 1, 32, 'uint256')
+ parsedType = decodeInfo.parseType(stateDef[5].attributes.type, states, 'contractSmallVariable', util.extractLocationFromAstVariable(stateDef[5]))
+ checkDecodeInfo(st, parsedType, 1, 2, 'int16')
+
+ output = compiler.compileStandardWrapper(compilerInput(simplecontracts))
+ output = JSON.parse(output)
+
+ state = astHelper.extractStateDefinitions('test.sol:simpleContract', output.sources)
+ states = astHelper.extractStatesDefinitions(output.sources)
+ stateDef = state.stateDefinitions
+ parsedType = decodeInfo.parseType(stateDef[2].attributes.type, states, 'simpleContract', util.extractLocationFromAstVariable(stateDef[2]))
+ checkDecodeInfo(st, parsedType, 2, 32, 'struct simpleContract.structDef')
+ parsedType = decodeInfo.parseType(stateDef[3].attributes.type, states, 'simpleContract', util.extractLocationFromAstVariable(stateDef[3]))
+ checkDecodeInfo(st, parsedType, 6, 32, 'struct simpleContract.structDef[3]')
+ parsedType = decodeInfo.parseType(stateDef[4].attributes.type, states, 'simpleContract', util.extractLocationFromAstVariable(stateDef[4]))
+ checkDecodeInfo(st, parsedType, 1, 1, 'enum')
+
+ state = astHelper.extractStateDefinitions('test.sol:test2', output.sources)
+ stateDef = state.stateDefinitions
+ parsedType = decodeInfo.parseType(stateDef[0].attributes.type, states, 'test1', util.extractLocationFromAstVariable(stateDef[0]))
+ checkDecodeInfo(st, parsedType, 0, 32, 'struct test1.str')
+
+ state = stateDecoder.extractStateVariables('test.sol:test2', output.sources)
+ checkDecodeInfo(st, parsedType, 0, 32, 'struct test1.str')
+
+ st.end()
+ })
+})
+
+function checkDecodeInfo (st, decodeInfo, storageSlots, storageBytes, typeName) {
+ st.equal(decodeInfo.storageSlots, storageSlots)
+ st.equal(decodeInfo.storageBytes, storageBytes)
+ st.equal(decodeInfo.typeName, typeName)
+}
diff --git a/remix-debug/test/decoder/localDecoder.js b/remix-debug/test/decoder/localDecoder.js
new file mode 100644
index 0000000000..53d5006a46
--- /dev/null
+++ b/remix-debug/test/decoder/localDecoder.js
@@ -0,0 +1,41 @@
+'use strict'
+var tape = require('tape')
+var compiler = require('solc')
+var intLocal = require('./contracts/intLocal')
+var miscLocal = require('./contracts/miscLocal')
+var structArrayLocal = require('./contracts/structArrayLocal')
+var remixLib = require('remix-lib')
+var vmCall = require('./vmCall')
+var intLocalTest = require('./localsTests/int')
+var miscLocalTest = require('./localsTests/misc')
+var misc2LocalTest = require('./localsTests/misc2')
+var structArrayLocalTest = require('./localsTests/structArray')
+var compilerInput = remixLib.helpers.compiler.compilerInput
+
+tape('solidity', function (t) {
+ t.test('local decoder', function (st) {
+ var privateKey = new Buffer('dae9801649ba2d95a21e688b56f77905e5667c44ce868ec83f82e838712a2c7a', 'hex')
+ var vm = vmCall.initVM(st, privateKey)
+ test(st, vm, privateKey)
+ })
+})
+
+function test (st, vm, privateKey) {
+ var output = compiler.compileStandardWrapper(compilerInput(intLocal.contract))
+ output = JSON.parse(output)
+ intLocalTest(st, vm, privateKey, output.contracts['test.sol']['intLocal'].evm.bytecode.object, output, function () {
+ output = compiler.compileStandardWrapper(compilerInput(miscLocal.contract))
+ output = JSON.parse(output)
+ miscLocalTest(st, vm, privateKey, output.contracts['test.sol']['miscLocal'].evm.bytecode.object, output, function () {
+ output = compiler.compileStandardWrapper(compilerInput(miscLocal.contract))
+ output = JSON.parse(output)
+ misc2LocalTest(st, vm, privateKey, output.contracts['test.sol']['miscLocal2'].evm.bytecode.object, output, function () {
+ output = compiler.compileStandardWrapper(compilerInput(structArrayLocal.contract))
+ output = JSON.parse(output)
+ structArrayLocalTest(st, vm, privateKey, output.contracts['test.sol']['structArrayLocal'].evm.bytecode.object, output, function () {
+ st.end()
+ })
+ })
+ })
+ })
+}
diff --git a/remix-debug/test/decoder/localsTests/helper.js b/remix-debug/test/decoder/localsTests/helper.js
new file mode 100644
index 0000000000..867f515d85
--- /dev/null
+++ b/remix-debug/test/decoder/localsTests/helper.js
@@ -0,0 +1,29 @@
+'use strict'
+var localDecoder = require('../../../src/decoder/localDecoder')
+
+/*
+ Decode local variable
+*/
+function decodeLocal (st, index, traceManager, callTree, verifier) {
+ try {
+ traceManager.waterfall([
+ traceManager.getStackAt,
+ traceManager.getMemoryAt],
+ index,
+ function (error, result) {
+ if (!error) {
+ localDecoder.solidityLocals(index, callTree, result[0].value, result[1].value, {}, {start: 5000}).then((locals) => {
+ verifier(locals)
+ })
+ } else {
+ st.fail(error)
+ }
+ })
+ } catch (e) {
+ st.fail(e.message)
+ }
+}
+
+module.exports = {
+ decodeLocals: decodeLocal
+}
diff --git a/remix-debug/test/decoder/localsTests/int.js b/remix-debug/test/decoder/localsTests/int.js
new file mode 100644
index 0000000000..db3a35f6c8
--- /dev/null
+++ b/remix-debug/test/decoder/localsTests/int.js
@@ -0,0 +1,112 @@
+'use strict'
+
+var TraceManager = require('../../../src/trace/traceManager')
+var CodeManager = require('../../../src/code/codeManager')
+
+var vmCall = require('../vmCall')
+var remixLib = require('remix-lib')
+
+var traceHelper = remixLib.helpers.trace
+var SolidityProxy = require('../../../src/decoder/solidityProxy')
+var InternalCallTree = require('../../../src/decoder/internalCallTree')
+var EventManager = remixLib.EventManager
+var helper = require('./helper')
+
+module.exports = function (st, vm, privateKey, contractBytecode, compilationResult, cb) {
+ vmCall.sendTx(vm, {nonce: 0, privateKey: privateKey}, null, 0, contractBytecode, function (error, txHash) {
+ if (error) {
+ st.fail(error)
+ } else {
+ vm.web3.eth.getTransaction(txHash, function (error, tx) {
+ if (error) {
+ st.fail(error)
+ } else {
+ tx.to = traceHelper.contractCreationToken('0')
+ var traceManager = new TraceManager({web3: vm.web3})
+ var codeManager = new CodeManager(traceManager)
+ codeManager.clear()
+ var solidityProxy = new SolidityProxy(traceManager, codeManager)
+ solidityProxy.reset(compilationResult)
+ var debuggerEvent = new EventManager()
+ var callTree = new InternalCallTree(debuggerEvent, traceManager, solidityProxy, codeManager, { includeLocalVariables: true })
+ callTree.event.register('callTreeBuildFailed', (error) => {
+ st.fail(error)
+ })
+ callTree.event.register('callTreeNotReady', (reason) => {
+ st.fail(reason)
+ })
+ callTree.event.register('callTreeReady', (scopes, scopeStarts) => {
+ try {
+ st.equals(scopeStarts[0], '')
+ st.equals(scopeStarts[13], '1')
+ st.equals(scopeStarts[103], '2')
+ st.equals(scopeStarts[116], '2.1')
+ st.equals(scopeStarts[135], '3')
+ st.equals(scopeStarts[151], '4')
+ st.equals(scopeStarts[164], '4.1')
+ st.equals(scopes[''].locals['ui8'].type.typeName, 'uint8')
+ st.equals(scopes[''].locals['ui16'].type.typeName, 'uint16')
+ st.equals(scopes[''].locals['ui32'].type.typeName, 'uint32')
+ st.equals(scopes[''].locals['ui64'].type.typeName, 'uint64')
+ st.equals(scopes[''].locals['ui128'].type.typeName, 'uint128')
+ st.equals(scopes[''].locals['ui256'].type.typeName, 'uint256')
+ st.equals(scopes[''].locals['ui'].type.typeName, 'uint256')
+ st.equals(scopes[''].locals['i8'].type.typeName, 'int8')
+ st.equals(scopes[''].locals['i16'].type.typeName, 'int16')
+ st.equals(scopes[''].locals['i32'].type.typeName, 'int32')
+ st.equals(scopes[''].locals['i64'].type.typeName, 'int64')
+ st.equals(scopes[''].locals['i128'].type.typeName, 'int128')
+ st.equals(scopes[''].locals['i256'].type.typeName, 'int256')
+ st.equals(scopes[''].locals['i'].type.typeName, 'int256')
+ st.equals(scopes[''].locals['ishrink'].type.typeName, 'int32')
+ st.equals(scopes['2'].locals['ui8'].type.typeName, 'uint8')
+ st.equals(scopes['2.1'].locals['ui81'].type.typeName, 'uint8')
+ st.equals(scopes['3'].locals['ui81'].type.typeName, 'uint8')
+ st.equals(scopes['4'].locals['ui8'].type.typeName, 'uint8')
+ st.equals(scopes['4.1'].locals['ui81'].type.typeName, 'uint8')
+ } catch (e) {
+ st.fail(e.message)
+ }
+
+ helper.decodeLocals(st, 95, traceManager, callTree, function (locals) {
+ st.equals(Object.keys(locals).length, 16)
+ st.equals(locals['ui8'].value, '130')
+ st.equals(locals['ui16'].value, '456')
+ st.equals(locals['ui32'].value, '4356')
+ st.equals(locals['ui64'].value, '3543543543')
+ st.equals(locals['ui128'].value, '234567')
+ st.equals(locals['ui256'].value, '115792089237316195423570985008687907853269984665640564039457584007880697216513')
+ st.equals(locals['ui'].value, '123545666')
+ st.equals(locals['i8'].value, '-45')
+ st.equals(locals['i16'].value, '-1234')
+ st.equals(locals['i32'].value, '3455')
+ st.equals(locals['i64'].value, '-35566')
+ st.equals(locals['i128'].value, '-444444')
+ st.equals(locals['i256'].value, '3434343')
+ st.equals(locals['i'].value, '-32432423423')
+ st.equals(locals['ishrink'].value, '2')
+ })
+
+ helper.decodeLocals(st, 171, traceManager, callTree, function (locals) {
+ try {
+ st.equals(locals['ui8'].value, '123')
+ st.equals(Object.keys(locals).length, 1)
+ } catch (e) {
+ st.fail(e.message)
+ }
+ cb()
+ })
+ })
+ traceManager.resolveTrace(tx, (error, result) => {
+ if (error) {
+ st.fail(error)
+ } else {
+ debuggerEvent.trigger('newTraceLoaded', [traceManager.trace])
+ }
+ })
+ }
+ })
+ }
+ })
+}
+
diff --git a/remix-debug/test/decoder/localsTests/misc.js b/remix-debug/test/decoder/localsTests/misc.js
new file mode 100644
index 0000000000..9285ab31c8
--- /dev/null
+++ b/remix-debug/test/decoder/localsTests/misc.js
@@ -0,0 +1,78 @@
+'use strict'
+var TraceManager = require('../../../src/trace/traceManager')
+var CodeManager = require('../../../src/code/codeManager')
+var vmCall = require('../vmCall')
+var remixLib = require('remix-lib')
+var traceHelper = remixLib.helpers.trace
+var SolidityProxy = require('../../../src/decoder/solidityProxy')
+var InternalCallTree = require('../../../src/decoder/internalCallTree')
+var EventManager = remixLib.EventManager
+var helper = require('./helper')
+
+module.exports = function (st, vm, privateKey, contractBytecode, compilationResult, cb) {
+ vmCall.sendTx(vm, {nonce: 0, privateKey: privateKey}, null, 0, contractBytecode, function (error, txHash) {
+ if (error) {
+ st.fail(error)
+ } else {
+ vm.web3.eth.getTransaction(txHash, function (error, tx) {
+ if (error) {
+ st.fail(error)
+ } else {
+ tx.to = traceHelper.contractCreationToken('0')
+ var traceManager = new TraceManager({web3: vm.web3})
+ var codeManager = new CodeManager(traceManager)
+ codeManager.clear()
+ var solidityProxy = new SolidityProxy(traceManager, codeManager)
+ solidityProxy.reset(compilationResult)
+ var debuggerEvent = new EventManager()
+ var callTree = new InternalCallTree(debuggerEvent, traceManager, solidityProxy, codeManager, { includeLocalVariables: true })
+ callTree.event.register('callTreeBuildFailed', (error) => {
+ st.fail(error)
+ })
+ callTree.event.register('callTreeReady', (scopes, scopeStarts) => {
+ helper.decodeLocals(st, 73, traceManager, callTree, function (locals) {
+ try {
+ st.equals(locals['boolFalse'].value, false)
+ st.equals(locals['boolTrue'].value, true)
+ st.equals(locals['testEnum'].value, 'three')
+ st.equals(locals['sender'].value, '0x4B0897B0513FDC7C541B6D9D7E929C4E5364D2DB')
+ st.equals(locals['_bytes1'].value, '0x99')
+ st.equals(locals['__bytes1'].value, '0x99')
+ st.equals(locals['__bytes2'].value, '0x99AB')
+ st.equals(locals['__bytes4'].value, '0x99FA0000')
+ st.equals(locals['__bytes6'].value, '0x990000000000')
+ st.equals(locals['__bytes7'].value, '0x99356700000000')
+ st.equals(locals['__bytes8'].value, '0x99ABD41700000000')
+ st.equals(locals['__bytes9'].value, '0x99156744AF00000000')
+ st.equals(locals['__bytes13'].value, '0x99123423425300000000000000')
+ st.equals(locals['__bytes16'].value, '0x99AFAD23432400000000000000000000')
+ st.equals(locals['__bytes24'].value, '0x99AFAD234324000000000000000000000000000000000000')
+ st.equals(locals['__bytes32'].value, '0x9999ABD41799ABD4170000000000000000000000000000000000000000000000')
+ st.equals(Object.keys(locals).length, 16)
+ } catch (e) {
+ st.fail(e.message)
+ }
+ })
+
+ helper.decodeLocals(st, 7, traceManager, callTree, function (locals) {
+ try {
+ // st.equals(Object.keys(locals).length, 0)
+ st.equals(0, 0)
+ } catch (e) {
+ st.fail(e.message)
+ }
+ cb()
+ })
+ })
+ traceManager.resolveTrace(tx, (error, result) => {
+ if (error) {
+ st.fail(error)
+ } else {
+ debuggerEvent.trigger('newTraceLoaded', [traceManager.trace])
+ }
+ })
+ }
+ })
+ }
+ })
+}
diff --git a/remix-debug/test/decoder/localsTests/misc2.js b/remix-debug/test/decoder/localsTests/misc2.js
new file mode 100644
index 0000000000..19b69eba04
--- /dev/null
+++ b/remix-debug/test/decoder/localsTests/misc2.js
@@ -0,0 +1,64 @@
+'use strict'
+var TraceManager = require('../../../src/trace/traceManager')
+var CodeManager = require('../../../src/code/codeManager')
+var vmCall = require('../vmCall')
+var remixLib = require('remix-lib')
+var traceHelper = remixLib.helpers.trace
+var SolidityProxy = require('../../../src/decoder/solidityProxy')
+var InternalCallTree = require('../../../src/decoder/internalCallTree')
+var EventManager = remixLib.EventManager
+var helper = require('./helper')
+
+module.exports = function (st, vm, privateKey, contractBytecode, compilationResult, cb) {
+ vmCall.sendTx(vm, {nonce: 0, privateKey: privateKey}, null, 0, contractBytecode, function (error, txHash) {
+ if (error) {
+ st.fail(error)
+ } else {
+ vm.web3.eth.getTransaction(txHash, function (error, tx) {
+ if (error) {
+ st.fail(error)
+ } else {
+ tx.to = traceHelper.contractCreationToken('0')
+ var traceManager = new TraceManager({web3: vm.web3})
+ var codeManager = new CodeManager(traceManager)
+ codeManager.clear()
+ var solidityProxy = new SolidityProxy(traceManager, codeManager)
+ solidityProxy.reset(compilationResult)
+ var debuggerEvent = new EventManager()
+ var callTree = new InternalCallTree(debuggerEvent, traceManager, solidityProxy, codeManager, { includeLocalVariables: true })
+ callTree.event.register('callTreeBuildFailed', (error) => {
+ st.fail(error)
+ })
+ callTree.event.register('callTreeReady', (scopes, scopeStarts) => {
+ helper.decodeLocals(st, 51, traceManager, callTree, function (locals) {
+ try {
+ st.equals(locals['dynbytes'].value, '0x64796e616d69636279746573')
+ st.equals(locals['smallstring'].value, 'test_test_test')
+ st.equals(Object.keys(locals).length, 2)
+ } catch (e) {
+ st.fail(e.message)
+ }
+ })
+
+ helper.decodeLocals(st, 7, traceManager, callTree, function (locals) {
+ try {
+ // st.equals(Object.keys(locals).length, 0)
+ st.equals(0, 0)
+ } catch (e) {
+ st.fail(e.message)
+ }
+ cb()
+ })
+ })
+ traceManager.resolveTrace(tx, (error, result) => {
+ if (error) {
+ st.fail(error)
+ } else {
+ debuggerEvent.trigger('newTraceLoaded', [traceManager.trace])
+ }
+ })
+ }
+ })
+ }
+ })
+}
diff --git a/remix-debug/test/decoder/localsTests/structArray.js b/remix-debug/test/decoder/localsTests/structArray.js
new file mode 100644
index 0000000000..984a341546
--- /dev/null
+++ b/remix-debug/test/decoder/localsTests/structArray.js
@@ -0,0 +1,122 @@
+'use strict'
+var TraceManager = require('../../../src/trace/traceManager')
+var CodeManager = require('../../../src/code/codeManager')
+var vmCall = require('../vmCall')
+var remixLib = require('remix-lib')
+var traceHelper = remixLib.helpers.trace
+var SolidityProxy = require('../../../src/decoder/solidityProxy')
+var InternalCallTree = require('../../../src/decoder/internalCallTree')
+var EventManager = remixLib.EventManager
+var helper = require('./helper')
+
+module.exports = function (st, vm, privateKey, contractBytecode, compilationResult, cb) {
+ vmCall.sendTx(vm, {nonce: 0, privateKey: privateKey}, null, 0, contractBytecode, function (error, txHash) {
+ if (error) {
+ st.fail(error)
+ } else {
+ vm.web3.eth.getTransaction(txHash, function (error, tx) {
+ if (error) {
+ st.fail(error)
+ } else {
+ tx.to = traceHelper.contractCreationToken('0')
+ var traceManager = new TraceManager({web3: vm.web3})
+ var codeManager = new CodeManager(traceManager)
+ codeManager.clear()
+ var solidityProxy = new SolidityProxy(traceManager, codeManager)
+ solidityProxy.reset(compilationResult)
+ var debuggerEvent = new EventManager()
+ var callTree = new InternalCallTree(debuggerEvent, traceManager, solidityProxy, codeManager, { includeLocalVariables: true })
+ callTree.event.register('callTreeBuildFailed', (error) => {
+ st.fail(error)
+ })
+ callTree.event.register('callTreeReady', (scopes, scopeStarts) => {
+ helper.decodeLocals(st, 1699, traceManager, callTree, function (locals) {
+ try {
+ st.equals(locals['bytesSimple'].length, '0x14')
+ st.equals(locals['bytesSimple'].value, '0x746573745f7375706572')
+ st.equals(locals['e'].value['a'].value, 'test')
+ st.equals(locals['e'].value['a'].length, '0x8')
+ st.equals(locals['e'].value['a'].raw, '0x74657374')
+ st.equals(locals['e'].value['b'].value, '5')
+ st.equals(locals['e'].value['c'].length, '0x220')
+ st.equals(locals['e'].value['c'].raw, '0x746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374')
+ st.equals(locals['e'].value['c'].value, 'test_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_test')
+ st.equals(locals['e'].value['d'].value, '3')
+ st.equals(locals['f'].length, '0x1b8')
+ st.equals(locals['f'].raw, '0x746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f')
+ st.equals(locals['f'].value, 'test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_')
+ st.equals(locals['e'].value['e'].value, true)
+
+ st.equals(locals['simpleArray'].value[0].value, '45')
+ st.equals(locals['simpleArray'].value[1].value, '324324')
+ st.equals(locals['simpleArray'].value[2].value, '-333')
+ st.equals(locals['simpleArray'].value[3].value, '5656')
+ st.equals(locals['simpleArray'].value[4].value, '-1111')
+
+ st.equals(locals['stringArray'].value[0].value, 'long_one_long_one_long_one_long_one_long_one_long_one_long_one_long_one_long_one_long_one_long_one_long_one_long_one_long_one_long_one_')
+ st.equals(locals['stringArray'].value[1].value, 'two')
+ st.equals(locals['stringArray'].value[2].value, 'three')
+
+ st.equals(locals['dynArray'].value[0].value[0].value, '3423423532')
+ st.equals(locals['dynArray'].value[1].value[0].value, '-342343323532')
+ st.equals(locals['dynArray'].value[1].value[1].value, '23432')
+ st.equals(locals['dynArray'].value[2].value[0].value, '-432432')
+ st.equals(locals['dynArray'].value[2].value[1].value, '3423423532')
+ st.equals(locals['dynArray'].value[2].value[2].value, '-432432')
+
+ st.equals(locals['structArray'].value[0].value['a'].value, 'test')
+ st.equals(locals['structArray'].value[0].value['a'].length, '0x8')
+ st.equals(locals['structArray'].value[0].value['a'].raw, '0x74657374')
+ st.equals(locals['structArray'].value[0].value['b'].value, '5')
+ st.equals(locals['structArray'].value[0].value['c'].length, '0x220')
+ st.equals(locals['structArray'].value[0].value['c'].raw, '0x746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374')
+ st.equals(locals['structArray'].value[0].value['c'].value, 'test_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_test')
+ st.equals(locals['structArray'].value[0].value['d'].value, '3')
+ st.equals(locals['structArray'].value[0].value['e'].value, true)
+
+ st.equals(locals['structArray'].value[1].value['a'].value, 'item1 a')
+ st.equals(locals['structArray'].value[1].value['b'].value, '20')
+ st.equals(locals['structArray'].value[1].value['c'].value, 'item1 c')
+ st.equals(locals['structArray'].value[1].value['d'].value, '-45')
+ st.equals(locals['structArray'].value[1].value['e'].value, false)
+
+ st.equals(locals['structArray'].value[2].value['a'].value, 'item2 a')
+ st.equals(locals['structArray'].value[2].value['b'].value, '200')
+ st.equals(locals['structArray'].value[2].value['c'].value, 'item2 c')
+ st.equals(locals['structArray'].value[2].value['d'].value, '-450')
+ st.equals(locals['structArray'].value[2].value['e'].value, true)
+
+ st.equals(locals['arrayStruct'].value.a.value[0].value, 'string')
+ st.equals(locals['arrayStruct'].value.b.value[0].value, '34')
+ st.equals(locals['arrayStruct'].value.b.value[1].value, '-23')
+ st.equals(locals['arrayStruct'].value.b.value[2].value, '-3')
+ st.equals(locals['arrayStruct'].value.c.value, 'three')
+
+ st.equals(Object.keys(locals).length, 8)
+ } catch (e) {
+ st.fail(e.message)
+ }
+ })
+
+ helper.decodeLocals(st, 7, traceManager, callTree, function (locals) {
+ try {
+ st.equals(0, 0)
+ // st.equals(Object.keys(locals).length, 0)
+ } catch (e) {
+ st.fail(e.message)
+ }
+ cb()
+ })
+ })
+ traceManager.resolveTrace(tx, (error, result) => {
+ if (error) {
+ st.fail(error)
+ } else {
+ debuggerEvent.trigger('newTraceLoaded', [traceManager.trace])
+ }
+ })
+ }
+ })
+ }
+ })
+}
diff --git a/remix-debug/test/decoder/mockStorageResolver.js b/remix-debug/test/decoder/mockStorageResolver.js
new file mode 100644
index 0000000000..a41d4f32f8
--- /dev/null
+++ b/remix-debug/test/decoder/mockStorageResolver.js
@@ -0,0 +1,39 @@
+'use strict'
+var remixLib = require('remix-lib')
+var util = remixLib.util
+
+class MockStorageResolver {
+ constructor (_storage) {
+ this.storage = {}
+ for (var k in _storage) {
+ var hashed = util.sha3_256(k)
+ this.storage[hashed] = {
+ hashed: hashed,
+ key: k,
+ value: _storage[k]
+ }
+ }
+ }
+
+ storageRange (callback) {
+ callback(null, this.storage)
+ }
+
+ storageSlot (slot, callback) {
+ var hashed = util.sha3_256(slot)
+ callback(null, this.storage[hashed])
+ }
+
+ isComplete (address) {
+ return true
+ }
+
+ fromCache (address, slotKey) {
+ return this.storage[slotKey]
+ }
+
+ toCache (address, storage, complete) {
+ }
+}
+
+module.exports = MockStorageResolver
diff --git a/remix-debug/test/decoder/stateTests/mapping.js b/remix-debug/test/decoder/stateTests/mapping.js
new file mode 100644
index 0000000000..ed14992837
--- /dev/null
+++ b/remix-debug/test/decoder/stateTests/mapping.js
@@ -0,0 +1,75 @@
+var remixLib = require('remix-lib')
+var compilerInput = remixLib.helpers.compiler.compilerInput
+var compiler = require('solc')
+var stateDecoder = require('../../../src/decoder/stateDecoder')
+var vmCall = require('../vmCall')
+
+var TraceManager = require('../../../src/trace/traceManager')
+var StorageResolver = require('../../../src/storage/storageResolver')
+var StorageViewer = require('../../../src/storage/storageViewer')
+
+module.exports = function testMappingStorage (st, cb) {
+ var mappingStorage = require('../contracts/mappingStorage')
+ var privateKey = new Buffer('dae9801649ba2d95a21e688b56f77905e5667c44ce868ec83f82e838712a2c7a', 'hex')
+ var vm = vmCall.initVM(st, privateKey)
+ var output = compiler.compileStandardWrapper(compilerInput(mappingStorage.contract))
+ output = JSON.parse(output)
+ vmCall.sendTx(vm, {nonce: 0, privateKey: privateKey}, null, 0, output.contracts['test.sol']['SimpleMappingState'].evm.bytecode.object, function (error, txHash) {
+ if (error) {
+ console.log(error)
+ st.end(error)
+ } else {
+ vm.web3.eth.getTransaction(txHash, (error, tx) => {
+ if (error) {
+ console.log(error)
+ st.end(error)
+ } else {
+ testMapping(st, vm, privateKey, tx.contractAddress, output, cb)
+ }
+ })
+ }
+ })
+}
+
+function testMapping (st, vm, privateKey, contractAddress, output, cb) {
+ vmCall.sendTx(vm, {nonce: 1, privateKey: privateKey}, contractAddress, 0, '2fd0a83a00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000001074686973206973206120737472696e6700000000000000000000000000000000',
+ function (error, txHash) {
+ if (error) {
+ console.log(error)
+ st.end(error)
+ } else {
+ console.log(txHash)
+ vm.web3.eth.getTransaction(txHash, (error, tx) => {
+ if (error) {
+ console.log(error)
+ st.end(error)
+ } else {
+ var traceManager = new TraceManager({web3: vm.web3})
+ traceManager.resolveTrace(tx, () => {
+ var storageViewer = new StorageViewer({
+ stepIndex: 213,
+ tx: tx,
+ address: contractAddress
+ }, new StorageResolver({web3: vm.web3}), traceManager)
+ var stateVars = stateDecoder.extractStateVariables('SimpleMappingState', output.sources)
+ stateDecoder.decodeState(stateVars, storageViewer).then((result) => {
+ console.log('ok', JSON.stringify(result))
+ st.equal(result['_num'].value, '1')
+ st.equal(result['_num'].type, 'uint256')
+ st.equal(result['_iBreakSolidityState'].type, 'mapping(string => uint256)')
+ st.equal(result['_iBreakSolidityState'].value['74686973206973206120737472696e67'].value, '1')
+ st.equal(result['_iBreakSolidityState'].value['74686973206973206120737472696e67'].type, 'uint256')
+ st.equal(result['_iBreakSolidityStateInt'].type, 'mapping(uint256 => uint256)')
+ st.equal(result['_iBreakSolidityStateInt'].value['0000000000000000000000000000000000000000000000000000000000000001'].value, '1')
+ st.equal(result['_iBreakSolidityStateInt'].value['0000000000000000000000000000000000000000000000000000000000000001'].type, 'uint256')
+ cb()
+ }, (reason) => {
+ console.log('fail')
+ st.end(reason)
+ })
+ })
+ }
+ })
+ }
+ })
+}
diff --git a/remix-debug/test/decoder/storageDecoder.js b/remix-debug/test/decoder/storageDecoder.js
new file mode 100644
index 0000000000..1c943e47fd
--- /dev/null
+++ b/remix-debug/test/decoder/storageDecoder.js
@@ -0,0 +1,276 @@
+'use strict'
+var tape = require('tape')
+var compiler = require('solc')
+var stateDecoder = require('../../src/decoder/stateDecoder')
+var MockStorageResolver = require('./mockStorageResolver')
+var remixLib = require('remix-lib')
+var compilerInput = remixLib.helpers.compiler.compilerInput
+var testMappingStorage = require('./stateTests/mapping')
+
+tape('solidity', function (t) {
+ t.test('storage decoder', function (st) {
+ testIntStorage(st, function () {
+ testByteStorage(st, function () {
+ testStructArrayStorage(st, function () {
+ testMappingStorage(st, function () {
+ st.end()
+ })
+ })
+ })
+ })
+ })
+})
+
+function testIntStorage (st, cb) {
+ var intStorage = require('./contracts/intStorage')
+ var output = compiler.compileStandardWrapper(compilerInput(intStorage.contract))
+ output = JSON.parse(output)
+ var mockStorageResolver
+ for (var storage of [intStorage.fullStorage, shrinkStorage(intStorage.fullStorage)]) {
+ mockStorageResolver = new MockStorageResolver(storage)
+ stateDecoder.solidityState(mockStorageResolver, output.sources, 'intStorage').then((decoded) => {
+ st.equal(decoded['ui8'].value, '130')
+ st.equal(decoded['ui16'].value, '456')
+ st.equal(decoded['ui32'].value, '4356')
+ st.equal(decoded['ui64'].value, '3543543543')
+ st.equal(decoded['ui128'].value, '234567')
+ st.equal(decoded['ui256'].value, '115792089237316195423570985008687907853269984665640564039457584007880697216513')
+ st.equal(decoded['ui'].value, '123545666')
+ st.equal(decoded['i8'].value, '-45')
+ st.equal(decoded['i16'].value, '-1234')
+ st.equal(decoded['i32'].value, '3455')
+ st.equal(decoded['i64'].value, '-35566')
+ st.equal(decoded['i128'].value, '-444444')
+ st.equal(decoded['i256'].value, '3434343')
+ st.equal(decoded['i'].value, '-32432423423')
+ st.equal(decoded['ishrink'].value, '2')
+ })
+ }
+
+ mockStorageResolver = new MockStorageResolver({})
+ stateDecoder.solidityState(mockStorageResolver, output.sources, 'intStorage').then((decoded) => {
+ st.equal(decoded['ui8'].value, '0')
+ st.equal(decoded['ui16'].value, '0')
+ st.equal(decoded['ui32'].value, '0')
+ st.equal(decoded['ui64'].value, '0')
+ st.equal(decoded['ui128'].value, '0')
+ st.equal(decoded['ui256'].value, '0')
+ st.equal(decoded['ui'].value, '0')
+ st.equal(decoded['i8'].value, '0')
+ st.equal(decoded['i16'].value, '0')
+ st.equal(decoded['i32'].value, '0')
+ st.equal(decoded['i64'].value, '0')
+ st.equal(decoded['i128'].value, '0')
+ st.equal(decoded['i256'].value, '0')
+ st.equal(decoded['i'].value, '0')
+ st.equal(decoded['ishrink'].value, '0')
+ cb()
+ })
+}
+
+function testByteStorage (st, cb) {
+ var byteStorage = require('./contracts/byteStorage')
+ var output = compiler.compileStandardWrapper(compilerInput(byteStorage.contract))
+ output = JSON.parse(output)
+ var mockStorageResolver
+ for (var storage of [byteStorage.storage, shrinkStorage(byteStorage.storage)]) {
+ mockStorageResolver = new MockStorageResolver(storage)
+ stateDecoder.solidityState(mockStorageResolver, output.sources, 'byteStorage').then((decoded) => {
+ st.equal(decoded['b1'].value, false)
+ st.equal(decoded['a1'].value, '0xFE350F199F244AC9A79038D254400B632A633225')
+ st.equal(decoded['b2'].value, true)
+ st.equal(decoded['dynb1'].value, '0x64796e616d69636279746573')
+ st.equal(decoded['dynb1'].length, '0xc')
+ st.equal(decoded['stab'].value, '0x01')
+ st.equal(decoded['stab1'].value, '0x12')
+ st.equal(decoded['stab2'].value, '0x1579')
+ st.equal(decoded['stab3'].value, '0x359356')
+ st.equal(decoded['stab4'].value, '0x23750000')
+ st.equal(decoded['stab5'].value, '0x0235764500')
+ st.equal(decoded['stab6'].value, '0x324435000000')
+ st.equal(decoded['stab7'].value, '0x00432400000000')
+ st.equal(decoded['stab8'].value, '0x3245546457650000')
+ st.equal(decoded['stab9'].value, '0x034345430000000000')
+ st.equal(decoded['stab10'].value, '0x04543543654657000000')
+ st.equal(decoded['stab11'].value, '0x5435465400000000000000')
+ st.equal(decoded['stab12'].value, '0x030000000000000000000000')
+ st.equal(decoded['stab13'].value, '0x03243242345435000000000000')
+ st.equal(decoded['stab14'].value, '0x3245435435435300000000000000')
+ st.equal(decoded['stab15'].value, '0x032454434435000000000000000000')
+ st.equal(decoded['stab16'].value, '0x32454354440000000000000000000000')
+ st.equal(decoded['stab17'].value, '0x0324543432432432450000000000000000')
+ st.equal(decoded['stab18'].value, '0x032453432543543500000000000000000000')
+ st.equal(decoded['stab19'].value, '0x03245434354354350000000000000000000000')
+ st.equal(decoded['stab20'].value, '0x032454543543AB35000000000000000000000000')
+ st.equal(decoded['stab21'].value, '0x324544324234350000000000000000000000000000')
+ st.equal(decoded['stab22'].value, '0x324543AEF50000000000000000000000000000000000')
+ st.equal(decoded['stab23'].value, '0x3245435FFF000000000000000000000000000000000000')
+ st.equal(decoded['stab24'].value, '0x3245435F0000000000000000000000000000000000000000')
+ st.equal(decoded['stab25'].value, '0x3245435F000000000000000000000000000000000000000000')
+ st.equal(decoded['stab26'].value, '0x3245435F00000000000000000000000000000000000000000000')
+ st.equal(decoded['stab27'].value, '0x03245FFFFFFF000000000000000000000000000000000000000000')
+ st.equal(decoded['stab28'].value, '0x03241235000000000000000000000000000000000000000000000000')
+ st.equal(decoded['stab29'].value, '0x0325213213000000000000000000000000000000000000000000000000')
+ st.equal(decoded['stab30'].value, '0x032454352324230000000000000000000000000000000000000000000000')
+ st.equal(decoded['stab31'].value, '0x32454351230000000000000000000000000000000000000000000000000000')
+ st.equal(decoded['stab32'].value, '0x324324423432543543AB00000000000000000000000000000000000000000000')
+ st.equal(decoded['enumDec'].value, 'e240')
+ st.equal(decoded['str1'].value, 'short')
+ st.equal(decoded['str12'].value, 'шеллы')
+ st.equal(decoded['str2'].value, 'long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long__long')
+ })
+ }
+
+ mockStorageResolver = new MockStorageResolver({})
+ stateDecoder.solidityState(mockStorageResolver, output.sources, 'byteStorage').then((decoded) => {
+ st.equal(decoded['b1'].value, false)
+ st.equal(decoded['a1'].value, '0x0000000000000000000000000000000000000000')
+ st.equal(decoded['b2'].value, false)
+ st.equal(decoded['dynb1'].value, '0x')
+ st.equal(decoded['dynb1'].length, '0x0')
+ st.equal(decoded['stab'].value, '0x00')
+ st.equal(decoded['stab1'].value, '0x00')
+ st.equal(decoded['stab2'].value, '0x0000')
+ st.equal(decoded['stab3'].value, '0x000000')
+ st.equal(decoded['stab4'].value, '0x00000000')
+ st.equal(decoded['stab5'].value, '0x0000000000')
+ st.equal(decoded['stab6'].value, '0x000000000000')
+ st.equal(decoded['stab7'].value, '0x00000000000000')
+ st.equal(decoded['stab8'].value, '0x0000000000000000')
+ st.equal(decoded['stab9'].value, '0x000000000000000000')
+ st.equal(decoded['stab10'].value, '0x00000000000000000000')
+ st.equal(decoded['stab11'].value, '0x0000000000000000000000')
+ st.equal(decoded['stab12'].value, '0x000000000000000000000000')
+ st.equal(decoded['stab13'].value, '0x00000000000000000000000000')
+ st.equal(decoded['stab14'].value, '0x0000000000000000000000000000')
+ st.equal(decoded['stab15'].value, '0x000000000000000000000000000000')
+ st.equal(decoded['stab16'].value, '0x00000000000000000000000000000000')
+ st.equal(decoded['stab17'].value, '0x0000000000000000000000000000000000')
+ st.equal(decoded['stab18'].value, '0x000000000000000000000000000000000000')
+ st.equal(decoded['stab19'].value, '0x00000000000000000000000000000000000000')
+ st.equal(decoded['stab20'].value, '0x0000000000000000000000000000000000000000')
+ st.equal(decoded['stab21'].value, '0x000000000000000000000000000000000000000000')
+ st.equal(decoded['stab22'].value, '0x00000000000000000000000000000000000000000000')
+ st.equal(decoded['stab23'].value, '0x0000000000000000000000000000000000000000000000')
+ st.equal(decoded['stab24'].value, '0x000000000000000000000000000000000000000000000000')
+ st.equal(decoded['stab25'].value, '0x00000000000000000000000000000000000000000000000000')
+ st.equal(decoded['stab26'].value, '0x0000000000000000000000000000000000000000000000000000')
+ st.equal(decoded['stab27'].value, '0x000000000000000000000000000000000000000000000000000000')
+ st.equal(decoded['stab28'].value, '0x00000000000000000000000000000000000000000000000000000000')
+ st.equal(decoded['stab29'].value, '0x0000000000000000000000000000000000000000000000000000000000')
+ st.equal(decoded['stab30'].value, '0x000000000000000000000000000000000000000000000000000000000000')
+ st.equal(decoded['stab31'].value, '0x00000000000000000000000000000000000000000000000000000000000000')
+ st.equal(decoded['stab32'].value, '0x0000000000000000000000000000000000000000000000000000000000000000')
+ st.equal(decoded['enumDec'].value, 'e0')
+ st.equal(decoded['str1'].length, '0x0')
+ st.equal(decoded['str2'].length, '0x0')
+ st.equal(decoded['str1'].value, '')
+ st.equal(decoded['str12'].value, '')
+ st.equal(decoded['str2'].value, '')
+ cb()
+ })
+}
+
+function shrinkStorage (storage) {
+ var shrinkedStorage = {}
+ var regex = /0x(00)*(..)/
+ for (var key in storage) {
+ var value = storage[key]
+ shrinkedStorage[key.replace(regex, '0x$2')] = value.replace(regex, '0x$2')
+ }
+ return shrinkedStorage
+}
+
+function testStructArrayStorage (st, cb) {
+ var structArrayStorage = require('./contracts/structArrayStorage')
+ var output = compiler.compileStandardWrapper(compilerInput(structArrayStorage.contract))
+ output = JSON.parse(output)
+ var mockStorageResolver = new MockStorageResolver(structArrayStorage.storage)
+ stateDecoder.solidityState(mockStorageResolver, output.sources, 'structArrayStorage').then((decoded) => {
+ st.equal(decoded['intStructDec'].value['i8'].value, '32')
+ st.equal(decoded['intStructDec'].value['i16'].value, '-54')
+ st.equal(decoded['intStructDec'].value['ui32'].value, '128')
+ st.equal(decoded['intStructDec'].value['i256'].value, '-1243565465756')
+ st.equal(decoded['intStructDec'].value['ui16'].value, '34556')
+ st.equal(decoded['intStructDec'].value['i32'].value, '-345446546')
+ st.equal(decoded['i5'].length, '0x7')
+ st.equal(decoded['i5'].value[0].value, '-2134')
+ st.equal(decoded['i5'].value[1].value, '345')
+ st.equal(decoded['i5'].value[2].value, '-3246')
+ st.equal(decoded['i5'].value[3].value, '4357')
+ st.equal(decoded['i5'].value[4].value, '324')
+ st.equal(decoded['i5'].value[5].value, '-2344')
+ st.equal(decoded['i5'].value[6].value, '3254')
+ st.equal(decoded['idyn5'].length, '0x9')
+ st.equal(decoded['idyn5'].value[0].value, '-2134')
+ st.equal(decoded['idyn5'].value[1].value, '345')
+ st.equal(decoded['idyn5'].value[2].value, '-3246')
+ st.equal(decoded['idyn5'].value[3].value, '4357')
+ st.equal(decoded['idyn5'].value[4].value, '324')
+ st.equal(decoded['idyn5'].value[5].value, '-2344')
+ st.equal(decoded['idyn5'].value[6].value, '3254')
+ st.equal(decoded['idyn5'].value[7].value, '-254')
+ st.equal(decoded['idyn5'].value[8].value, '-2354')
+ st.equal(decoded['dyn1'].length, '0x4')
+ st.equal(decoded['dyn1'].value[0].length, '0x1')
+ st.equal(decoded['dyn1'].value[0].value[0].value, '3')
+ st.equal(decoded['dyn1'].value[1].length, '0x3')
+ st.equal(decoded['dyn1'].value[1].value[0].value, '12')
+ st.equal(decoded['dyn1'].value[1].value[1].value, '-12')
+ st.equal(decoded['dyn1'].value[1].value[2].value, '-1234')
+ st.equal(decoded['dyn1'].value[2].length, '0xa')
+ st.equal(decoded['dyn1'].value[2].value[0].value, '1')
+ st.equal(decoded['dyn1'].value[2].value[1].value, '12')
+ st.equal(decoded['dyn1'].value[2].value[2].value, '1235')
+ st.equal(decoded['dyn1'].value[2].value[3].value, '-12')
+ st.equal(decoded['dyn1'].value[2].value[4].value, '-123456')
+ st.equal(decoded['dyn1'].value[2].value[5].value, '-23435435')
+ st.equal(decoded['dyn1'].value[2].value[6].value, '543543')
+ st.equal(decoded['dyn1'].value[2].value[7].value, '2')
+ st.equal(decoded['dyn1'].value[2].value[8].value, '-1')
+ st.equal(decoded['dyn1'].value[2].value[9].value, '232432')
+ st.equal(decoded['dyn1'].value[3].length, '0x2')
+ st.equal(decoded['dyn1'].value[3].value[0].value, '232432')
+ st.equal(decoded['dyn1'].value[3].value[0].value, '232432')
+ st.equal(decoded['dyn2'].length, '0x2')
+ st.equal(decoded['dyn2'].value[0].length, '0x4')
+ st.equal(decoded['dyn2'].value[0].value[0].value[0].value, '23')
+ st.equal(decoded['dyn2'].value[0].value[0].value[1].value, '-23')
+ st.equal(decoded['dyn2'].value[0].value[0].value[2].value, '-28')
+ st.equal(decoded['dyn2'].value[0].value[1].value[0].value, '23')
+ st.equal(decoded['dyn2'].value[0].value[1].value[1].value, '-23')
+ st.equal(decoded['dyn2'].value[0].value[1].value[2].value, '-28')
+ st.equal(decoded['dyn2'].value[0].value[2].value[0].value, '23')
+ st.equal(decoded['dyn2'].value[0].value[2].value[1].value, '-23')
+ st.equal(decoded['dyn2'].value[0].value[2].value[2].value, '-28')
+ st.equal(decoded['dyn2'].value[0].value[3].value[0].value, '23')
+ st.equal(decoded['dyn2'].value[0].value[3].value[1].value, '-23')
+ st.equal(decoded['dyn2'].value[0].value[3].value[2].value, '-28')
+ st.equal(decoded['dyn2'].value[1].length, '0x4')
+ st.equal(decoded['dyn2'].value[1].value[0].value[0].value, '23')
+ st.equal(decoded['dyn2'].value[1].value[0].value[1].value, '-23')
+ st.equal(decoded['dyn2'].value[1].value[0].value[2].value, '-28')
+ st.equal(decoded['dyn2'].value[1].value[1].value[0].value, '23')
+ st.equal(decoded['dyn2'].value[1].value[1].value[1].value, '-23')
+ st.equal(decoded['dyn2'].value[1].value[1].value[2].value, '-28')
+ st.equal(decoded['dyn2'].value[1].value[2].value[0].value, '23')
+ st.equal(decoded['dyn2'].value[1].value[2].value[1].value, '-23')
+ st.equal(decoded['dyn2'].value[1].value[2].value[2].value, '-28')
+ st.equal(decoded['dyn2'].value[1].value[3].value[0].value, '23')
+ st.equal(decoded['dyn2'].value[1].value[3].value[1].value, '-23')
+ st.equal(decoded['dyn2'].value[1].value[3].value[2].value, '-28')
+ st.equal(decoded['arrayStruct'].value[0].value[0].value.i8.value, '34')
+ st.equal(decoded['arrayStruct'].value[0].value[0].value.str.value, 'test_str_short')
+ st.equal(decoded['arrayStruct'].value[0].value[1].value.i8.value, '-123')
+ st.equal(decoded['arrayStruct'].value[0].value[1].value.str.value, 'test_str_long test_str_lo ngtest_str_longtest_str_ longtest_str_longtest_ str_longtest_str_l ongtest_str_long')
+ st.equal(decoded['arrayStruct'].value[1].value[0].value.i8.value, '50')
+ st.equal(decoded['arrayStruct'].value[1].value[0].value.str.value, 'test_str_short')
+ st.equal(decoded['arrayStruct'].value[2].value[0].value.i8.value, '60')
+ st.equal(decoded['arrayStruct'].value[2].value[0].value.str.value, 'test_str_short')
+ st.equal(decoded['arrayStruct'].value[2].value[1].value.i8.value, '84')
+ st.equal(decoded['arrayStruct'].value[2].value[1].value.str.value, 'test_str_long test_str_lo ngtest_str_longtest_str_ longtest_str_longtest_ str_longtest_str_l ongtest_str_long')
+ st.equal(decoded['arrayStruct'].value[2].value[2].value.i8.value, '-34')
+ st.equal(decoded['arrayStruct'].value[2].value[2].value.str.value, 'test_str_short')
+ cb()
+ })
+}
diff --git a/remix-debug/test/decoder/storageLocation.js b/remix-debug/test/decoder/storageLocation.js
new file mode 100644
index 0000000000..fe4dc601d3
--- /dev/null
+++ b/remix-debug/test/decoder/storageLocation.js
@@ -0,0 +1,59 @@
+'use strict'
+var tape = require('tape')
+var compiler = require('solc')
+var stateDecoder = require('../../src/decoder/stateDecoder')
+var contracts = require('./contracts/miscContracts')
+var remixLib = require('remix-lib')
+var compilerInput = remixLib.helpers.compiler.compilerInput
+
+tape('solidity', function (t) {
+ t.test('storage location', function (st) {
+ var output = compiler.compileStandardWrapper(compilerInput(contracts))
+ output = JSON.parse(output)
+ var stateDec = stateDecoder.extractStateVariables('contractUint', output.sources)
+ checkLocation(st, stateDec[0].storagelocation, 0, 0)
+ checkLocation(st, stateDec[1].storagelocation, 1, 0)
+ checkLocation(st, stateDec[2].storagelocation, 2, 0)
+ checkLocation(st, stateDec[3].storagelocation, 3, 0)
+
+ stateDec = stateDecoder.extractStateVariables('contractStructAndArray', output.sources)
+ checkLocation(st, stateDec[0].storagelocation, 0, 0)
+ checkLocation(st, stateDec[1].storagelocation, 2, 0)
+ checkLocation(st, stateDec[2].storagelocation, 8, 0)
+
+ stateDec = stateDecoder.extractStateVariables('contractArray', output.sources)
+ checkLocation(st, stateDec[0].storagelocation, 0, 0)
+ checkLocation(st, stateDec[1].storagelocation, 1, 0)
+ checkLocation(st, stateDec[2].storagelocation, 2, 0)
+
+ stateDec = stateDecoder.extractStateVariables('contractSmallVariable', output.sources)
+ checkLocation(st, stateDec[0].storagelocation, 0, 0)
+ checkLocation(st, stateDec[1].storagelocation, 0, 1)
+ checkLocation(st, stateDec[2].storagelocation, 0, 2)
+ checkLocation(st, stateDec[3].storagelocation, 0, 4)
+ checkLocation(st, stateDec[4].storagelocation, 1, 0)
+ checkLocation(st, stateDec[5].storagelocation, 2, 0)
+
+ stateDec = stateDecoder.extractStateVariables('testSimpleStorage', output.sources)
+ checkLocation(st, stateDec[0].storagelocation, 0, 0)
+ checkLocation(st, stateDec[1].storagelocation, 1, 0)
+ checkLocation(st, stateDec[2].storagelocation, 2, 0)
+ checkLocation(st, stateDec[3].storagelocation, 3, 0)
+ checkLocation(st, stateDec[4].storagelocation, 4, 0)
+ checkLocation(st, stateDec[5].storagelocation, 8, 0)
+ checkLocation(st, stateDec[6].storagelocation, 9, 0)
+ checkLocation(st, stateDec[8].storagelocation, 17, 0)
+ checkLocation(st, stateDec[9].storagelocation, 17, 4)
+ checkLocation(st, stateDec[10].storagelocation, 17, 6)
+ checkLocation(st, stateDec[11].storagelocation, 17, 7)
+ checkLocation(st, stateDec[12].storagelocation, 18, 0)
+ checkLocation(st, stateDec[13].storagelocation, 21, 0)
+
+ st.end()
+ })
+})
+
+function checkLocation (st, location, slot, offset) {
+ st.equal(location.offset, offset)
+ st.equal(location.slot, slot)
+}
diff --git a/remix-debug/test/decoder/vmCall.js b/remix-debug/test/decoder/vmCall.js
new file mode 100644
index 0000000000..a57e3114c5
--- /dev/null
+++ b/remix-debug/test/decoder/vmCall.js
@@ -0,0 +1,63 @@
+'use strict'
+var utileth = require('ethereumjs-util')
+var Tx = require('ethereumjs-tx')
+var Block = require('ethereumjs-block')
+var BN = require('ethereumjs-util').BN
+var remixLib = require('remix-lib')
+
+function sendTx (vm, from, to, value, data, cb) {
+ var tx = new Tx({
+ nonce: new BN(from.nonce++),
+ gasPrice: new BN(1),
+ gasLimit: new BN(3000000, 10),
+ to: to,
+ value: new BN(value, 10),
+ data: new Buffer(data, 'hex')
+ })
+ tx.sign(from.privateKey)
+ var block = new Block({
+ header: {
+ timestamp: new Date().getTime() / 1000 | 0,
+ number: 0
+ },
+ transactions: [],
+ uncleHeaders: []
+ })
+ vm.runTx({block: block, tx: tx, skipBalance: true, skipNonce: true}, function (error, result) {
+ setTimeout(() => {
+ cb(error, utileth.bufferToHex(tx.hash()))
+ }, 500)
+ })
+}
+
+/*
+ Init VM / Send Transaction
+*/
+function initVM (st, privateKey) {
+ var utileth = require('ethereumjs-util')
+ var VM = require('ethereumjs-vm')
+ var Web3Providers = remixLib.vm.Web3Providers
+ var address = utileth.privateToAddress(privateKey)
+ var vm = new VM({
+ enableHomestead: true,
+ activatePrecompiles: true
+ })
+ vm.stateManager.putAccountBalance(address, 'f00000000000000001', function cb () {})
+ var web3Providers = new Web3Providers()
+ web3Providers.addVM('VM', vm)
+ web3Providers.get('VM', function (error, obj) {
+ if (error) {
+ var mes = 'provider TEST not defined'
+ console.log(mes)
+ st.fail(mes)
+ } else {
+ vm.web3 = obj
+ }
+ })
+ return vm
+}
+
+module.exports = {
+ sendTx: sendTx,
+ initVM: initVM
+}
diff --git a/remix-debug/test/disassembler.js b/remix-debug/test/disassembler.js
new file mode 100644
index 0000000000..d0c617f010
--- /dev/null
+++ b/remix-debug/test/disassembler.js
@@ -0,0 +1,101 @@
+'use strict'
+
+var tape = require('tape')
+var disassemble = require('../src/code/disassembler').disassemble
+
+tape('Disassembler', function (t) {
+ t.test('empty', function (st) {
+ st.plan(1)
+ st.equal(disassemble(''), '')
+ })
+ t.test('add', function (st) {
+ st.plan(1)
+ st.equal(disassemble('0x01'), 'add')
+ })
+ t.test('push', function (st) {
+ st.plan(1)
+ st.equal(disassemble('0x640203'), '0x0203000000')
+ })
+ t.test('complexcode', function (st) {
+ st.plan(1)
+ var code = '60606040526009600060005055607e8060186000396000f360606040526000357c0100000000000000000000000000000000000000000000000000000000900480630dbe671f146039576035565b6002565b3460025760486004805050604a565b005b6000600090505b600a811015607a5760006000818150548092919060010191905055505b80806001019150506051565b5b5056'
+ var asm = `mstore(0x40, 0x60)
+0x09
+0x00
+pop(0x00)
+sstore
+0x7e
+dup1
+0x18
+0x00
+codecopy
+0x00
+return
+mstore(0x40, 0x60)
+calldataload(0x00)
+0x0100000000000000000000000000000000000000000000000000000000
+swap1
+div
+dup1
+0x0dbe671f
+eq
+0x39
+jumpi
+jump(0x35)
+label1:
+jump(0x02)
+label2:
+jumpi(0x02, callvalue())
+0x48
+0x04
+dup1
+pop
+pop
+jump(0x4a)
+label3:
+stop()
+label4:
+0x00
+0x00
+swap1
+pop
+label5:
+0x0a
+dup2
+lt
+iszero
+0x7a
+jumpi
+0x00
+0x00
+dup2
+dup2
+pop
+sload
+dup1
+swap3
+swap2
+swap1
+0x01
+add
+swap2
+swap1
+pop
+sstore
+pop
+label6:
+dup1
+dup1
+0x01
+add
+swap2
+pop
+pop
+jump(0x51)
+label7:
+label8:
+pop
+jump`
+ st.equal(disassemble(code), asm)
+ })
+})
diff --git a/test/init.js b/remix-debug/test/init.js
similarity index 92%
rename from test/init.js
rename to remix-debug/test/init.js
index 46db217f3a..bd8f9114ea 100644
--- a/test/init.js
+++ b/remix-debug/test/init.js
@@ -2,7 +2,7 @@ var init = {
overrideWeb3: function (web3, web3Override) {
web3.eth.getCode = web3Override.getCode
web3.debug.traceTransaction = web3Override.traceTransaction
- web3.debug.storageAt = web3Override.storageAt
+ web3.debug.storageRangeAt = web3Override.storageRangeAt
web3.eth.getTransaction = web3Override.getTransaction
web3.eth.getTransactionFromBlock = web3Override.getTransactionFromBlock
web3.eth.getBlockNumber = web3Override.getBlockNumber
diff --git a/test/resources/testWeb3.js b/remix-debug/test/resources/testWeb3.js
similarity index 88%
rename from test/resources/testWeb3.js
rename to remix-debug/test/resources/testWeb3.js
index f5d8bbb4e7..a138a81eb4 100644
--- a/test/resources/testWeb3.js
+++ b/remix-debug/test/resources/testWeb3.js
@@ -4,7 +4,7 @@ var web3Override = {}
web3Override.eth = {}
web3Override.debug = {}
var data = init.readFile(require('path').resolve(__dirname, 'testWeb3.json'))
-var data = JSON.parse(data)
+data = JSON.parse(data)
web3Override.eth.getCode = function (address, callback) {
if (callback) {
@@ -18,8 +18,8 @@ web3Override.debug.traceTransaction = function (txHash, options, callback) {
callback(null, data.testTraces[txHash])
}
-web3Override.debug.storageAt = function (blockNumber, txIndex, address, callback) {
- callback(null, {})
+web3Override.debug.storageRangeAt = function (blockNumber, txIndex, address, start, maxSize, callback) {
+ callback(null, { storage: {}, complete: true })
}
web3Override.eth.getTransaction = function (txHash, callback) {
diff --git a/test/resources/testWeb3.json b/remix-debug/test/resources/testWeb3.json
similarity index 100%
rename from test/resources/testWeb3.json
rename to remix-debug/test/resources/testWeb3.json
diff --git a/remix-debug/test/tests.js b/remix-debug/test/tests.js
new file mode 100644
index 0000000000..37481f6501
--- /dev/null
+++ b/remix-debug/test/tests.js
@@ -0,0 +1,194 @@
+'use strict'
+var tape = require('tape')
+var remixLib = require('remix-lib')
+var compilerInput = remixLib.helpers.compiler.compilerInput
+var vmCall = require('./vmCall')
+var Debugger = require('../src/Ethdebugger')
+var compiler = require('solc')
+
+require('./traceManager.js')
+require('./codeManager.js')
+require('./disassembler.js')
+
+require('./decoder/decodeInfo.js')
+require('./decoder/storageLocation.js')
+require('./decoder/storageDecoder.js')
+require('./decoder/localDecoder.js')
+
+var BreakpointManager = require('../src/code/breakpointManager')
+
+tape('debug contract', function (t) {
+ t.plan(12)
+ var privateKey = new Buffer('dae9801649ba2d95a21e688b56f77905e5667c44ce868ec83f82e838712a2c7a', 'hex')
+ var vm = vmCall.initVM(t, privateKey)
+ var output = compiler.compileStandardWrapper(compilerInput(ballot))
+ output = JSON.parse(output)
+ var web3VM = new remixLib.vm.Web3VMProvider()
+ web3VM.setVM(vm)
+ vmCall.sendTx(vm, {nonce: 0, privateKey: privateKey}, null, 0, output.contracts['test.sol']['Ballot'].evm.bytecode.object, (error, txHash) => {
+ if (error) {
+ t.end(error)
+ } else {
+ web3VM.eth.getTransaction(txHash, (error, tx) => {
+ if (error) {
+ t.end(error)
+ } else {
+ var debugManager = new Debugger({
+ compilationResult: function () {
+ return output
+ }
+ })
+
+ debugManager.addProvider('web3vmprovider', web3VM)
+ debugManager.switchProvider('web3vmprovider')
+
+ debugManager.callTree.event.register('callTreeReady', () => {
+ testDebugging(t, debugManager)
+ })
+
+ debugManager.debug(tx)
+ }
+ })
+ }
+ })
+})
+
+
+function testDebugging (t, debugManager) {
+ // stack
+ debugManager.traceManager.getStackAt(4, (error, callstack) => {
+ if (error) return t.end(error)
+ t.equal(JSON.stringify(callstack), JSON.stringify([ '0x0000000000000000000000000000000000000000000000000000000000000000' ]))
+ })
+
+ debugManager.traceManager.getStackAt(41, (error, callstack) => {
+ if (error) return t.end(error)
+
+ /*
+ t.equal(JSON.stringify(callstack), JSON.stringify(['0x0000000000000000000000000000000000000000000000000000000000000000', '0x0000000000000000000000004b0897b0513fdc7c541b6d9d7e929c4e5364d2db', '0x0000000000000000000000000000000000000000000000000000000000000000', '0x0000000000000000000000000000000000000000000000000000000000000001', '0x000000000000000000000000000000000000000000000000000000000000002d']))
+ */
+ })
+
+ // storage
+ debugManager.traceManager.getCurrentCalledAddressAt(38, (error, address) => {
+ if (error) return t.end(error)
+ var storageView = debugManager.storageViewAt(38, address)
+ storageView.storageRange((error, storage) => {
+ if (error) return t.end(error)
+ t.equal(JSON.stringify(storage), JSON.stringify({ '0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563': { key: '0x0000000000000000000000000000000000000000000000000000000000000000', value: '0x0000000000000000000000004b0897b0513fdc7c541b6d9d7e929c4e5364d2db' } }))
+ })
+ })
+
+ debugManager.extractStateAt(116, (error, state) => {
+ if (error) return t.end(error)
+ debugManager.decodeStateAt(116, state, (error, decodedState) => {
+ if (error) return t.end(error)
+ t.equal(decodedState['chairperson'].value, '0x4B0897B0513FDC7C541B6D9D7E929C4E5364D2DB')
+ t.equal(decodedState['chairperson'].type, 'address')
+ t.equal(decodedState['proposals'].value[0].value.voteCount.value, '0')
+ t.equal(decodedState['proposals'].value[0].value.voteCount.type, 'uint256')
+ t.equal(decodedState['proposals'].value[0].type, 'struct Ballot.Proposal')
+ t.equal(decodedState['proposals'].length, '0x1')
+ t.equal(decodedState['proposals'].type, 'struct Ballot.Proposal[]')
+ })
+ })
+
+ debugManager.traceManager.getCurrentCalledAddressAt(104, (error, address) => {
+ if (error) return t.end(error)
+ debugManager.sourceLocationFromVMTraceIndex(address, 104, (error, location) => {
+ if (error) return t.end(error)
+ debugManager.decodeLocalsAt(104, location, (error, decodedlocals) => {
+ if (error) return t.end(error)
+ t.equal(JSON.stringify(decodedlocals), JSON.stringify({'p': {'value': '45', 'type': 'uint256'}, 'addressLocal': {'value': '0x4B0897B0513FDC7C541B6D9D7E929C4E5364D2DB', 'type': 'address'}, 'proposalsLocals': {'value': [{'value': {'voteCount': {'value': '0', 'type': 'uint256'}}, 'type': 'struct Ballot.Proposal'}], 'length': '0x1', 'type': 'struct Ballot.Proposal[]'}}))
+ })
+ })
+ })
+
+ var sourceMappingDecoder = new remixLib.SourceMappingDecoder()
+ var breakPointManager = new BreakpointManager(debugManager, (rawLocation) => {
+ return sourceMappingDecoder.convertOffsetToLineColumn(rawLocation, sourceMappingDecoder.getLinebreakPositions(ballot))
+ })
+
+ breakPointManager.add({fileName: 'test.sol', row: 23})
+
+ breakPointManager.event.register('breakpointHit', function (sourceLocation, step) {
+ console.log('breakpointHit')
+ t.equal(JSON.stringify(sourceLocation), JSON.stringify({ start: 591, length: 1, file: 0, jump: '-' }))
+ t.equal(step, 75)
+ })
+
+ breakPointManager.event.register('noBreakpointHit', function () {
+ t.end('noBreakpointHit')
+ console.log('noBreakpointHit')
+ })
+ breakPointManager.jumpNextBreakpoint(0, true)
+}
+
+var ballot = `pragma solidity ^0.4.0;
+contract Ballot {
+
+ struct Voter {
+ uint weight;
+ bool voted;
+ uint8 vote;
+ address delegate;
+ }
+ struct Proposal {
+ uint voteCount;
+ }
+
+ address chairperson;
+ mapping(address => Voter) voters;
+ Proposal[] proposals;
+
+ /// Create a new ballot with $(_numProposals) different proposals.
+ function Ballot() public {
+ uint p = 45;
+ chairperson = msg.sender;
+ address addressLocal = msg.sender; // copy of state variable
+ voters[chairperson].weight = 1;
+ proposals.length = 1;
+ Proposal[] proposalsLocals = proposals; // copy of state variable
+ }
+
+ /// Give $(toVoter) the right to vote on this ballot.
+ /// May only be called by $(chairperson).
+ function giveRightToVote(address toVoter) public {
+ if (msg.sender != chairperson || voters[toVoter].voted) return;
+ voters[toVoter].weight = 1;
+ }
+
+ /// Delegate your vote to the voter $(to).
+ function delegate(address to) public {
+ Voter storage sender = voters[msg.sender]; // assigns reference
+ if (sender.voted) return;
+ while (voters[to].delegate != address(0) && voters[to].delegate != msg.sender)
+ to = voters[to].delegate;
+ if (to == msg.sender) return;
+ sender.voted = true;
+ sender.delegate = to;
+ Voter storage delegateTo = voters[to];
+ if (delegateTo.voted)
+ proposals[delegateTo.vote].voteCount += sender.weight;
+ else
+ delegateTo.weight += sender.weight;
+ }
+
+ /// Give a single vote to proposal $(toProposal).
+ function vote(uint8 toProposal) public {
+ Voter storage sender = voters[msg.sender];
+ if (sender.voted || toProposal >= proposals.length) return;
+ sender.voted = true;
+ sender.vote = toProposal;
+ proposals[toProposal].voteCount += sender.weight;
+ }
+
+ function winningProposal() public constant returns (uint8 _winningProposal) {
+ uint256 winningVoteCount = 0;
+ for (uint8 prop = 0; prop < proposals.length; prop++)
+ if (proposals[prop].voteCount > winningVoteCount) {
+ winningVoteCount = proposals[prop].voteCount;
+ _winningProposal = prop;
+ }
+ }
+}`
diff --git a/test/traceManager.js b/remix-debug/test/traceManager.js
similarity index 87%
rename from test/traceManager.js
rename to remix-debug/test/traceManager.js
index facb586eca..053b07ea3c 100644
--- a/test/traceManager.js
+++ b/remix-debug/test/traceManager.js
@@ -1,10 +1,12 @@
'use strict'
var TraceManager = require('../src/trace/traceManager')
var tape = require('tape')
-var Web3Providers = require('../src/web3Provider/web3Providers')
-var util = require('../src/helpers/global')
+var remixLib = require('remix-lib')
+var Web3Providers = remixLib.vm.Web3Providers
var web3Test = require('./resources/testWeb3')
+let web3 = null
+
tape('TraceManager', function (t) {
var traceManager
@@ -17,15 +19,15 @@ tape('TraceManager', function (t) {
console.log(mes)
st.fail(mes)
} else {
- util.web3 = obj
- traceManager = new TraceManager()
+ web3 = obj
+ traceManager = new TraceManager({web3: web3})
st.end()
}
})
})
t.test('TraceManager.resolveTrace', function (st) {
- var tx = util.web3.eth.getTransaction('0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd51')
+ var tx = web3.eth.getTransaction('0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd51')
traceManager.resolveTrace(tx, function (error, result) {
if (error) {
st.fail(' - traceManager.resolveTrace - failed ' + result)
@@ -53,13 +55,12 @@ tape('TraceManager', function (t) {
st.end()
})
- t.test('TraceManager.getStorageAt', function (st) {
- var tx = util.web3.eth.getTransaction('0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd51')
- traceManager.getStorageAt(110, tx, function (error, result) {
+ t.test('TraceManager.accumulateStorageChanges', function (st) {
+ traceManager.accumulateStorageChanges(110, '0x0d3a18d64dfe4f927832ab58d6451cecc4e517c5', {}, function (error, result) {
if (error) {
st.fail(error)
} else {
- st.ok(result['0x00'] === '0x38')
+ st.ok(result['0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563'].value === '0x38')
st.end()
}
})
@@ -125,7 +126,7 @@ tape('TraceManager', function (t) {
if (error) {
st.fail(error)
} else {
- st.ok(result === 0)
+ st.ok(result.start === 0)
}
})
@@ -134,7 +135,7 @@ tape('TraceManager', function (t) {
if (error) {
st.fail(error)
} else {
- st.ok(result === 64)
+ st.ok(result.start === 64)
}
})
@@ -143,7 +144,9 @@ tape('TraceManager', function (t) {
if (error) {
st.fail(error)
} else {
- st.ok(result === 109)
+ st.ok(result.start === 0)
+ // this was 109 before: 111 is targeting the root call (starting index 0)
+ // this test make more sense as it is now (109 is the index of RETURN).
}
})
})
@@ -275,28 +278,14 @@ tape('TraceManager', function (t) {
t.test('TraceManager.findStepOverBack', function (st) {
var result = traceManager.findStepOverBack(116)
console.log(result)
- st.ok(result === -1)
+ st.ok(result === 115)
st.end()
})
t.test('TraceManager.findStepOverForward', function (st) {
var result = traceManager.findStepOverForward(66)
console.log(result)
- st.ok(result === 108)
- st.end()
- })
-
- t.test('TraceManager.findStepOutBack', function (st) {
- var result = traceManager.findStepOutBack(70)
- console.log(result)
- st.ok(result === 63)
- st.end()
- })
-
- t.test('TraceManager.findStepOutForward', function (st) {
- var result = traceManager.findStepOutForward(15)
- console.log(result)
- st.ok(result === 142)
+ st.ok(result === 67)
st.end()
})
diff --git a/remix-debug/test/vmCall.js b/remix-debug/test/vmCall.js
new file mode 100644
index 0000000000..a57e3114c5
--- /dev/null
+++ b/remix-debug/test/vmCall.js
@@ -0,0 +1,63 @@
+'use strict'
+var utileth = require('ethereumjs-util')
+var Tx = require('ethereumjs-tx')
+var Block = require('ethereumjs-block')
+var BN = require('ethereumjs-util').BN
+var remixLib = require('remix-lib')
+
+function sendTx (vm, from, to, value, data, cb) {
+ var tx = new Tx({
+ nonce: new BN(from.nonce++),
+ gasPrice: new BN(1),
+ gasLimit: new BN(3000000, 10),
+ to: to,
+ value: new BN(value, 10),
+ data: new Buffer(data, 'hex')
+ })
+ tx.sign(from.privateKey)
+ var block = new Block({
+ header: {
+ timestamp: new Date().getTime() / 1000 | 0,
+ number: 0
+ },
+ transactions: [],
+ uncleHeaders: []
+ })
+ vm.runTx({block: block, tx: tx, skipBalance: true, skipNonce: true}, function (error, result) {
+ setTimeout(() => {
+ cb(error, utileth.bufferToHex(tx.hash()))
+ }, 500)
+ })
+}
+
+/*
+ Init VM / Send Transaction
+*/
+function initVM (st, privateKey) {
+ var utileth = require('ethereumjs-util')
+ var VM = require('ethereumjs-vm')
+ var Web3Providers = remixLib.vm.Web3Providers
+ var address = utileth.privateToAddress(privateKey)
+ var vm = new VM({
+ enableHomestead: true,
+ activatePrecompiles: true
+ })
+ vm.stateManager.putAccountBalance(address, 'f00000000000000001', function cb () {})
+ var web3Providers = new Web3Providers()
+ web3Providers.addVM('VM', vm)
+ web3Providers.get('VM', function (error, obj) {
+ if (error) {
+ var mes = 'provider TEST not defined'
+ console.log(mes)
+ st.fail(mes)
+ } else {
+ vm.web3 = obj
+ }
+ })
+ return vm
+}
+
+module.exports = {
+ sendTx: sendTx,
+ initVM: initVM
+}
diff --git a/remix-debugger/README.md b/remix-debugger/README.md
new file mode 100644
index 0000000000..2da32212e5
--- /dev/null
+++ b/remix-debugger/README.md
@@ -0,0 +1,74 @@
+# `remix-debugger`
+# (Remix debugger has been deprecated and is not maintained anymore - the `remix-debug` module can be used to build your own 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/remix-debugger/assets/css/font-awesome.min.css b/remix-debugger/assets/css/font-awesome.min.css
new file mode 100644
index 0000000000..540440ce89
--- /dev/null
+++ b/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/remix-debugger/assets/fonts/FontAwesome.otf b/remix-debugger/assets/fonts/FontAwesome.otf
new file mode 100644
index 0000000000..681bdd4d4c
Binary files /dev/null and b/remix-debugger/assets/fonts/FontAwesome.otf differ
diff --git a/remix-debugger/assets/fonts/fontawesome-webfont.eot b/remix-debugger/assets/fonts/fontawesome-webfont.eot
new file mode 100644
index 0000000000..a30335d748
Binary files /dev/null and b/remix-debugger/assets/fonts/fontawesome-webfont.eot differ
diff --git a/remix-debugger/assets/fonts/fontawesome-webfont.svg b/remix-debugger/assets/fonts/fontawesome-webfont.svg
new file mode 100644
index 0000000000..6fd19abcb9
--- /dev/null
+++ b/remix-debugger/assets/fonts/fontawesome-webfont.svg
@@ -0,0 +1,640 @@
+
+
+
\ No newline at end of file
diff --git a/remix-debugger/assets/fonts/fontawesome-webfont.ttf b/remix-debugger/assets/fonts/fontawesome-webfont.ttf
new file mode 100644
index 0000000000..d7994e1308
Binary files /dev/null and b/remix-debugger/assets/fonts/fontawesome-webfont.ttf differ
diff --git a/remix-debugger/assets/fonts/fontawesome-webfont.woff b/remix-debugger/assets/fonts/fontawesome-webfont.woff
new file mode 100644
index 0000000000..6fd4ede0f3
Binary files /dev/null and b/remix-debugger/assets/fonts/fontawesome-webfont.woff differ
diff --git a/remix-debugger/assets/fonts/fontawesome-webfont.woff2 b/remix-debugger/assets/fonts/fontawesome-webfont.woff2
new file mode 100644
index 0000000000..5560193ccc
Binary files /dev/null and b/remix-debugger/assets/fonts/fontawesome-webfont.woff2 differ
diff --git a/remix-debugger/ci/browser_tests.sh b/remix-debugger/ci/browser_tests.sh
new file mode 100755
index 0000000000..b395c03481
--- /dev/null
+++ b/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/ci/deploy_from_travis.sh b/remix-debugger/ci/deploy_from_travis.sh
similarity index 78%
rename from ci/deploy_from_travis.sh
rename to remix-debugger/ci/deploy_from_travis.sh
index b64364610e..e80aba8e47 100755
--- a/ci/deploy_from_travis.sh
+++ b/remix-debugger/ci/deploy_from_travis.sh
@@ -11,7 +11,15 @@ 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
-git add -f README.md index.html build/app.js
+
+# 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
diff --git a/ci/deploy_key.enc b/remix-debugger/ci/deploy_key.enc
similarity index 100%
rename from ci/deploy_key.enc
rename to remix-debugger/ci/deploy_key.enc
diff --git a/ci/sauceDisconnect.js b/remix-debugger/ci/sauceDisconnect.js
similarity index 100%
rename from ci/sauceDisconnect.js
rename to remix-debugger/ci/sauceDisconnect.js
diff --git a/findClient.js b/remix-debugger/findClient.js
similarity index 100%
rename from findClient.js
rename to remix-debugger/findClient.js
diff --git a/index.html b/remix-debugger/index.html
similarity index 51%
rename from index.html
rename to remix-debugger/index.html
index 26cf0b6c91..4c6c33cf4c 100644
--- a/index.html
+++ b/remix-debugger/index.html
@@ -1,15 +1,16 @@
+
diff --git a/remix-debugger/index.js b/remix-debugger/index.js
new file mode 100644
index 0000000000..a113f9390d
--- /dev/null
+++ b/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/nightwatch.js b/remix-debugger/nightwatch.js
similarity index 74%
rename from nightwatch.js
rename to remix-debugger/nightwatch.js
index c21e2a7282..b81c8176b5 100644
--- a/nightwatch.js
+++ b/remix-debugger/nightwatch.js
@@ -1,9 +1,9 @@
'use strict'
-var TRAVIS_JOB_NUMBER = process.env.TRAVIS_JOB_NUMBER
+var buildId = process.env.CIRCLE_BUILD_NUM || process.env.TRAVIS_JOB_NUMBER
module.exports = {
- 'src_folders': ['./test-browser'],
- 'output_folder': './test-browser/reports',
+ 'src_folders': ['./test-browser/test'],
+ 'output_folder': './test-browser/test/reports',
'custom_commands_path': '',
'custom_assertions_path': '',
'globals_path': '',
@@ -43,8 +43,8 @@ module.exports = {
'browserName': 'firefox',
'javascriptEnabled': true,
'acceptSslCerts': true,
- 'build': 'build-' + TRAVIS_JOB_NUMBER,
- 'tunnel-identifier': 'remix_tests_' + TRAVIS_JOB_NUMBER
+ 'build': 'build-' + buildId,
+ 'tunnel-identifier': 'remix_tests_' + buildId
}
},
@@ -53,8 +53,8 @@ module.exports = {
'browserName': 'chrome',
'javascriptEnabled': true,
'acceptSslCerts': true,
- 'build': 'build-' + TRAVIS_JOB_NUMBER,
- 'tunnel-identifier': 'remix_tests_' + TRAVIS_JOB_NUMBER
+ 'build': 'build-' + buildId,
+ 'tunnel-identifier': 'remix_tests_' + buildId
}
},
@@ -62,10 +62,11 @@ module.exports = {
'desiredCapabilities': {
'browserName': 'safari',
'javascriptEnabled': true,
- 'platform': 'MAC',
+ 'platform': 'OS X 10.11',
+ 'version': '10.0',
'acceptSslCerts': true,
- 'build': 'build-' + TRAVIS_JOB_NUMBER,
- 'tunnel-identifier': 'remix_tests_' + TRAVIS_JOB_NUMBER
+ 'build': 'build-' + buildId,
+ 'tunnel-identifier': 'remix_tests_' + buildId
}
},
@@ -74,8 +75,10 @@ module.exports = {
'browserName': 'internet explorer',
'javascriptEnabled': true,
'acceptSslCerts': true,
- 'build': 'build-' + TRAVIS_JOB_NUMBER,
- 'tunnel-identifier': 'remix_tests_' + TRAVIS_JOB_NUMBER
+ 'platform': 'WIN8.1',
+ 'version': '11',
+ 'build': 'build-' + buildId,
+ 'tunnel-identifier': 'remix_tests_' + buildId
}
},
diff --git a/remix-debugger/package.json b/remix-debugger/package.json
new file mode 100644
index 0000000000..a5cdaf390a
--- /dev/null
+++ b/remix-debugger/package.json
@@ -0,0 +1,150 @@
+{
+ "name": "remix-debugger",
+ "version": "0.1.9",
+ "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.3",
+ "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": "^0.0.15",
+ "remix-lib": "^0.2.9",
+ "remix-solidity": "^0.1.11",
+ "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/runNode.sh b/remix-debugger/runNode.sh
similarity index 100%
rename from runNode.sh
rename to remix-debugger/runNode.sh
diff --git a/src/ui/BasicPanel.js b/remix-debugger/src/ui/BasicPanel.js
similarity index 77%
rename from src/ui/BasicPanel.js
rename to remix-debugger/src/ui/BasicPanel.js
index aa4eae240d..6efed6231c 100644
--- a/src/ui/BasicPanel.js
+++ b/remix-debugger/src/ui/BasicPanel.js
@@ -1,7 +1,16 @@
'use strict'
var style = require('./styles/basicStyles')
var yo = require('yo-yo')
-var ui = require('../helpers/ui')
+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
@@ -24,7 +33,8 @@ BasicPanel.prototype.show = function () {
}
BasicPanel.prototype.render = function () {
- var view = yo`