diff --git a/.readthedocs.yml b/.readthedocs.yml new file mode 100644 index 0000000000..c0c139b7e8 --- /dev/null +++ b/.readthedocs.yml @@ -0,0 +1,18 @@ +version: 2 + +build: + os: ubuntu-22.04 + tools: + python: "3.11" + +sphinx: + builder: html + configuration: docs/conf.py + +formats: + - pdf + - epub + +python: + install: + - requirements: docs/requirements.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index b43f244949..b78269a3d6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,11 +1,11 @@ cmake_minimum_required(VERSION 3.13.0) -set(ETH_CMAKE_DIR "${CMAKE_CURRENT_LIST_DIR}/cmake" CACHE PATH "The the path to the cmake directory") +set(ETH_CMAKE_DIR "${CMAKE_CURRENT_LIST_DIR}/cmake" CACHE PATH "The path to the cmake directory") list(APPEND CMAKE_MODULE_PATH ${ETH_CMAKE_DIR}) # Set the build type, if none was specified. if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) - if(EXISTS "${CMAKE_SOURCE_DIR}/.git") + if(EXISTS "${PROJECT_SOURCE_DIR}/.git") set(DEFAULT_BUILD_TYPE "RelWithDebInfo") else() set(DEFAULT_BUILD_TYPE "Release") @@ -21,7 +21,7 @@ include(EthPolicy) eth_policy() # project name and version should be set after cmake_policy CMP0048 -set(PROJECT_VERSION "0.8.18") +set(PROJECT_VERSION "0.8.29") # OSX target needed in order to support std::visit set(CMAKE_OSX_DEPLOYMENT_TARGET "10.14") project(solidity VERSION ${PROJECT_VERSION} LANGUAGES C CXX) @@ -34,19 +34,34 @@ endif() option(SOLC_LINK_STATIC "Link solc executable statically on supported platforms" OFF) option(SOLC_STATIC_STDLIBS "Link solc against static versions of libgcc and libstdc++ on supported platforms" OFF) -option(STRICT_Z3_VERSION "Use the latest version of Z3" ON) +option(STRICT_Z3_VERSION "Require the exact version of Z3 solver expected by our test suite." ON) option(PEDANTIC "Enable extra warnings and pedantic build flags. Treat all warnings as errors." ON) option(PROFILE_OPTIMIZER_STEPS "Output performance metrics for the optimiser steps." OFF) +option( + IGNORE_VENDORED_DEPENDENCIES + "Ignore libraries provided as submodules of the repository and allow CMake to look for \ +them in the typical locations, including system-wide dirs." + OFF +) +option( + ONLY_BUILD_SOLIDITY_LIBRARIES + "Only build library targets that can be statically linked against. Do not build executables or tests." + OFF +) +mark_as_advanced(PROFILE_OPTIMIZER_STEPS) +mark_as_advanced(IGNORE_VENDORED_DEPENDENCIES) +mark_as_advanced(ONLY_BUILD_SOLIDITY_LIBRARIES) # Setup cccache. include(EthCcache) # Let's find our dependencies include(EthDependencies) -include(fmtlib) -include(jsoncpp) -include(range-v3) -include_directories(SYSTEM ${JSONCPP_INCLUDE_DIR}) +if (NOT IGNORE_VENDORED_DEPENDENCIES) + include(fmtlib) + include(nlohmann-json) + include(range-v3) +endif() find_package(Threads) @@ -66,24 +81,24 @@ include(EthUtils) # Create license.h from LICENSE.txt and template # Converting to char array is required due to MSVC's string size limit. -file(READ ${CMAKE_SOURCE_DIR}/LICENSE.txt LICENSE_TEXT HEX) +file(READ ${PROJECT_SOURCE_DIR}/LICENSE.txt LICENSE_TEXT HEX) string(REGEX MATCHALL ".." LICENSE_TEXT "${LICENSE_TEXT}") string(REGEX REPLACE ";" ",\n\t0x" LICENSE_TEXT "${LICENSE_TEXT}") set(LICENSE_TEXT "0x${LICENSE_TEXT}") -configure_file("${CMAKE_SOURCE_DIR}/cmake/templates/license.h.in" include/license.h) +configure_file("${PROJECT_SOURCE_DIR}/cmake/templates/license.h.in" include/license.h) include(EthOptions) configure_project(TESTS) -set(LATEST_Z3_VERSION "4.11.2") -set(MINIMUM_Z3_VERSION "4.8.0") +set(TESTED_Z3_VERSION "4.12.1") +set(MINIMUM_Z3_VERSION "4.8.16") find_package(Z3) if (${Z3_FOUND}) if (${STRICT_Z3_VERSION}) - if (NOT ("${Z3_VERSION_STRING}" VERSION_EQUAL ${LATEST_Z3_VERSION})) + if (NOT ("${Z3_VERSION_STRING}" VERSION_EQUAL ${TESTED_Z3_VERSION})) message( FATAL_ERROR - "SMTChecker tests require Z3 ${LATEST_Z3_VERSION} for all tests to pass.\n\ + "SMTChecker tests require Z3 ${TESTED_Z3_VERSION} for all tests to pass.\n\ Build with -DSTRICT_Z3_VERSION=OFF if you want to use a different version. \ You can also use -DUSE_Z3=OFF to build without Z3. In both cases use --no-smt when running tests." ) @@ -122,15 +137,11 @@ elseif (${Z3_FOUND}) message("Z3 SMT solver found. This enables optional SMT checking with Z3.") endif() -find_package(CVC4 QUIET) -if (${CVC4_FOUND}) - add_definitions(-DHAVE_CVC4) - message("CVC4 SMT solver found. This enables optional SMT checking with CVC4.") -endif() +find_program(CVC5_PATH cvc5) -if (NOT (${Z3_FOUND} OR ${CVC4_FOUND})) +if (NOT (${Z3_FOUND} OR CVC5_PATH)) message("No SMT solver found (or it has been forcefully disabled). Optional SMT checking will not be available.\ - \nPlease install Z3 or CVC4 or remove the option disabling them (USE_Z3, USE_CVC4).") + \nPlease install Z3 or cvc5 or remove the option disabling them (USE_Z3).") endif() add_subdirectory(libsolutil) @@ -140,12 +151,16 @@ add_subdirectory(libevmasm) add_subdirectory(libyul) add_subdirectory(libsolidity) add_subdirectory(libsolc) -add_subdirectory(tools) +add_subdirectory(libstdlib) -if (NOT EMSCRIPTEN) - add_subdirectory(solc) -endif() +if (NOT ONLY_BUILD_SOLIDITY_LIBRARIES) + add_subdirectory(tools) -if (TESTS AND NOT EMSCRIPTEN) - add_subdirectory(test) + if (NOT EMSCRIPTEN) + add_subdirectory(solc) + endif() + + if (TESTS AND NOT EMSCRIPTEN) + add_subdirectory(test) + endif() endif() diff --git a/docs/050-breaking-changes.rst b/docs/050-breaking-changes.rst index 3f41ac860a..a40bbcf6e9 100644 --- a/docs/050-breaking-changes.rst +++ b/docs/050-breaking-changes.rst @@ -137,7 +137,7 @@ For most of the topics the compiler will provide suggestions. ``payable`` or create a new internal function for the program logic that uses ``msg.value``. -* For clarity reasons, the command line interface now requires ``-`` if the +* For clarity reasons, the command-line interface now requires ``-`` if the standard input is used as source. Deprecated Elements @@ -147,18 +147,18 @@ This section lists changes that deprecate prior features or syntax. Note that many of these changes were already enabled in the experimental mode ``v0.5.0``. -Command Line and JSON Interfaces +Command-line and JSON Interfaces -------------------------------- -* The command line option ``--formal`` (used to generate Why3 output for +* The command-line option ``--formal`` (used to generate Why3 output for further formal verification) was deprecated and is now removed. A new formal verification module, the SMTChecker, is enabled via ``pragma experimental SMTChecker;``. -* The command line option ``--julia`` was renamed to ``--yul`` due to the +* The command-line option ``--julia`` was renamed to ``--yul`` due to the renaming of the intermediate language ``Julia`` to ``Yul``. -* The ``--clone-bin`` and ``--combined-json clone-bin`` command line options +* The ``--clone-bin`` and ``--combined-json clone-bin`` command-line options were removed. * Remappings with empty prefix are disallowed. diff --git a/docs/060-breaking-changes.rst b/docs/060-breaking-changes.rst index 4f5571d54a..e7c93057f6 100644 --- a/docs/060-breaking-changes.rst +++ b/docs/060-breaking-changes.rst @@ -12,7 +12,7 @@ For the full list check Changes the Compiler Might not Warn About ========================================= -This section lists changes where the behaviour of your code might +This section lists changes where the behavior of your code might change without the compiler telling you about it. * The resulting type of an exponentiation is the type of the base. It used to be the smallest type @@ -105,23 +105,23 @@ Interface Changes ================= This section lists changes that are unrelated to the language itself, but that have an effect on the interfaces of -the compiler. These may change the way how you use the compiler on the command line, how you use its programmable +the compiler. These may change the way how you use the compiler on the command-line, how you use its programmable interface, or how you analyze the output produced by it. New Error Reporter ~~~~~~~~~~~~~~~~~~ -A new error reporter was introduced, which aims at producing more accessible error messages on the command line. -It is enabled by default, but passing ``--old-reporter`` falls back to the the deprecated old error reporter. +A new error reporter was introduced, which aims at producing more accessible error messages on the command-line. +It is enabled by default, but passing ``--old-reporter`` falls back to the deprecated old error reporter. Metadata Hash Options ~~~~~~~~~~~~~~~~~~~~~ The compiler now appends the `IPFS `_ hash of the metadata file to the end of the bytecode by default (for details, see documentation on :doc:`contract metadata `). Before 0.6.0, the compiler appended the -`Swarm `_ hash by default, and in order to still support this behaviour, -the new command line option ``--metadata-hash`` was introduced. It allows you to select the hash to be produced and -appended, by passing either ``ipfs`` or ``swarm`` as value to the ``--metadata-hash`` command line option. +`Swarm `_ hash by default, and in order to still support this behavior, +the new command-line option ``--metadata-hash`` was introduced. It allows you to select the hash to be produced and +appended, by passing either ``ipfs`` or ``swarm`` as value to the ``--metadata-hash`` command-line option. Passing the value ``none`` completely removes the hash. These changes can also be used via the :ref:`Standard JSON Interface` and effect the metadata JSON generated by the compiler. diff --git a/docs/080-breaking-changes.rst b/docs/080-breaking-changes.rst index b322d2a489..9d1f3347b6 100644 --- a/docs/080-breaking-changes.rst +++ b/docs/080-breaking-changes.rst @@ -10,18 +10,18 @@ For the full list check Silent Changes of the Semantics =============================== -This section lists changes where existing code changes its behaviour without +This section lists changes where existing code changes its behavior without the compiler notifying you about it. * Arithmetic operations revert on underflow and overflow. You can use ``unchecked { ... }`` to use - the previous wrapping behaviour. + the previous wrapping behavior. Checks for overflow are very common, so we made them the default to increase readability of code, even if it comes at a slight increase of gas costs. * ABI coder v2 is activated by default. - You can choose to use the old behaviour using ``pragma abicoder v1;``. + You can choose to use the old behavior using ``pragma abicoder v1;``. The pragma ``pragma experimental ABIEncoderV2;`` is still valid, but it is deprecated and has no effect. If you want to be explicit, please use ``pragma abicoder v2;`` instead. @@ -57,7 +57,7 @@ New Restrictions This section lists changes that might cause existing contracts to not compile anymore. -* There are new restrictions related to explicit conversions of literals. The previous behaviour in +* There are new restrictions related to explicit conversions of literals. The previous behavior in the following cases was likely ambiguous: 1. Explicit conversions from negative literals and literals larger than ``type(uint160).max`` to @@ -106,7 +106,7 @@ This section lists changes that might cause existing contracts to not compile an * The global functions ``log0``, ``log1``, ``log2``, ``log3`` and ``log4`` have been removed. - These are low-level functions that were largely unused. Their behaviour can be accessed from inline assembly. + These are low-level functions that were largely unused. Their behavior can be accessed from inline assembly. * ``enum`` definitions cannot contain more than 256 members. @@ -155,7 +155,7 @@ Interface Changes ``storage-layout`` are sub-objects now. Before 0.8.0 they used to be serialised as strings. * The "legacy AST" has been removed (``--ast-json`` on the commandline interface and ``legacyAST`` for standard JSON). - Use the "compact AST" (``--ast-compact--json`` resp. ``AST``) as replacement. + Use the "compact AST" (``--ast-compact-json`` resp. ``AST``) as replacement. * The old error reporter (``--old-reporter``) has been removed. @@ -173,4 +173,4 @@ How to update your code - Change ``msg.sender.transfer(x)`` to ``payable(msg.sender).transfer(x)`` or use a stored variable of ``address payable`` type. - Change ``x**y**z`` to ``(x**y)**z``. - Use inline assembly as a replacement for ``log0``, ..., ``log4``. -- Negate unsigned integers by subtracting them from the maximum value of the type and adding 1 (e.g. ``type(uint256).max - x + 1``, while ensuring that `x` is not zero) +- Negate unsigned integers by subtracting them from the maximum value of the type and adding 1 (e.g. ``type(uint256).max - x + 1``, while ensuring that ``x`` is not zero) diff --git a/docs/Makefile b/docs/Makefile index 3cc98f6990..01660bd388 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -34,7 +34,6 @@ help: @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" @@ -116,12 +115,6 @@ latexpdf: $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." -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." - text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000000..a3b5f25aed --- /dev/null +++ b/docs/README.md @@ -0,0 +1,23 @@ +# Solidity Language Docs + +## Local environment setup + +1. Install python https://www.python.org/downloads/ +1. Install sphinx (the tool used to generate the docs) https://www.sphinx-doc.org/en/master/usage/installation.html + +Go to `/docs` and run `./docs.sh` to install dependencies and build the project: + +```sh +cd docs +./docs.sh +``` + +That will output the generated htmls under _build/ + +## Serve environment + +```py +python3 -m http.server -d _build/html --cgi 8080 +``` + +Visit dev server at http://localhost:8080 diff --git a/docs/_static/css/custom-dark.css b/docs/_static/css/custom-dark.css new file mode 100644 index 0000000000..044a8f800d --- /dev/null +++ b/docs/_static/css/custom-dark.css @@ -0,0 +1,595 @@ + + +/* DARK MODE STYLING */ + +/* code directives */ + +:root[style*=dark] .method dt, +:root[style*=dark] .class dt, +:root[style*=dark] .data dt, +:root[style*=dark] .attribute dt, +:root[style*=dark] .function dt, +:root[style*=dark] .classmethod dt, +:root[style*=dark] .exception dt, +:root[style*=dark] .descclassname, +:root[style*=dark] .descname { + background-color: #2d2d2d !important; +} + +:root[style*=dark] .rst-content dl:not(.docutils) dt { + background-color: #0008; + border-top: solid 3px #fff2; + border-left: solid 3px #fff2; +} + +:root[style*=dark] em.property { + color: #888888; +} + + +/* tables */ + +:root[style*=dark] .rst-content table.docutils td { + border: 0px; +} + +:root[style*=dark] .rst-content table.docutils:not(.field-list) tr:nth-child(2n-1) td { + background-color: #0002; +} + +:root[style*=dark] .rst-content pre { + background: none; +} + +/* inlined code highlights */ + +:root[style*=dark] .xref, +:root[style*=dark] .py-meth { + color: #aaddff !important; + font-weight: normal !important; +} + +/* highlight color search text */ + +:root[style*=dark] .rst-content .highlighted { + background: #ff5722; + box-shadow: 0 0 0 2px #f0978b; +} + +/* notes, warnings, hints */ + +:root[style*=dark] .hint .admonition-title { + background: #2aa87c !important; +} + +:root[style*=dark] .warning .admonition-title { + background: #cc4444 !important; +} + +:root[style*=dark] .admonition-title { + background: #3a7ca8 !important; +} + +:root[style*=dark] .admonition, +:root[style*=dark] .note { + background-color: #0008 !important; +} + + +/* table of contents */ + +:root[style*=dark] .sidebar { + background-color: #191919 !important; +} + +:root[style*=dark] .sidebar-title { + background-color: #2b2b2b !important; +} + +:root[style*=dark] .wy-menu-vertical code.docutils.literal.notranslate { + background: none !important; + border: none !important; +} + + +:root[style*=dark] .toc-backref { + color: grey !important; +} + +:root[style*=dark] .highlight { + background: #0008; + color: #f8f8f2 +} + +:root[style*=dark] .highlight .c { + color: #888 +} + + +/* Comment */ + +:root[style*=dark] .highlight .err { + color: #960050; + background-color: #1e0010 +} + + +/* Error */ + +:root[style*=dark] .highlight .k { + color: #66d9ef +} + + +/* Keyword */ + +:root[style*=dark] .highlight .l { + color: #ae81ff +} + + +/* Literal */ + +:root[style*=dark] .highlight .n { + color: #f8f8f2 +} + + +/* Name */ + +:root[style*=dark] .highlight .o { + color: #f92672 +} + + +/* Operator */ + +:root[style*=dark] .highlight .p { + color: #f8f8f2 +} + + +/* Punctuation */ + +:root[style*=dark] .highlight .ch { + color: #888 +} + + +/* Comment.Hashbang */ + +:root[style*=dark] .highlight .cm { + color: #888 +} + + +/* Comment.Multiline */ + +:root[style*=dark] .highlight .cp { + color: #888 +} + + +/* Comment.Preproc */ + +:root[style*=dark] .highlight .cpf { + color: #888 +} + + +/* Comment.PreprocFile */ + +:root[style*=dark] .highlight .c1 { + color: #888 +} + + +/* Comment.Single */ + +:root[style*=dark] .highlight .cs { + color: #888 +} + + +/* Comment.Special */ + +:root[style*=dark] .highlight .gd { + color: #f92672 +} + + +/* Generic.Deleted */ + +:root[style*=dark] .highlight .ge { + font-style: italic +} + + +/* Generic.Emph */ + +:root[style*=dark] .highlight .gi { + color: #a6e22e +} + + +/* Generic.Inserted */ + +:root[style*=dark] .highlight .gs { + font-weight: bold +} + + +/* Generic.Strong */ + +:root[style*=dark] .highlight .gu { + color: #888 +} + + +/* Generic.Subheading */ + +:root[style*=dark] .highlight .kc { + color: #66d9ef +} + + +/* Keyword.Constant */ + +:root[style*=dark] .highlight .kd { + color: #66d9ef +} + + +/* Keyword.Declaration */ + +:root[style*=dark] .highlight .kn { + color: #f92672 +} + + +/* Keyword.Namespace */ + +:root[style*=dark] .highlight .kp { + color: #66d9ef +} + + +/* Keyword.Pseudo */ + +:root[style*=dark] .highlight .kr { + color: #66d9ef +} + + +/* Keyword.Reserved */ + +:root[style*=dark] .highlight .kt { + color: #66d9ef +} + + +/* Keyword.Type */ + +:root[style*=dark] .highlight .ld { + color: #e6db74 +} + + +/* Literal.Date */ + +:root[style*=dark] .highlight .m { + color: #ae81ff +} + + +/* Literal.Number */ + +:root[style*=dark] .highlight .s { + color: #e6db74 +} + + +/* Literal.String */ + +:root[style*=dark] .highlight .na { + color: #a6e22e +} + + +/* Name.Attribute */ + +:root[style*=dark] .highlight .nb { + color: #f8f8f2 +} + + +/* Name.Builtin */ + +:root[style*=dark] .highlight .nc { + color: #a6e22e +} + + +/* Name.Class */ + +:root[style*=dark] .highlight .no { + color: #66d9ef +} + + +/* Name.Constant */ + +:root[style*=dark] .highlight .nd { + color: #a6e22e +} + + +/* Name.Decorator */ + +:root[style*=dark] .highlight .ni { + color: #f8f8f2 +} + + +/* Name.Entity */ + +:root[style*=dark] .highlight .ne { + color: #a6e22e +} + + +/* Name.Exception */ + +:root[style*=dark] .highlight .nf { + color: #a6e22e +} + + +/* Name.Function */ + +:root[style*=dark] .highlight .nl { + color: #f8f8f2 +} + + +/* Name.Label */ + +:root[style*=dark] .highlight .nn { + color: #f8f8f2 +} + + +/* Name.Namespace */ + +:root[style*=dark] .highlight .nx { + color: #a6e22e +} + + +/* Name.Other */ + +:root[style*=dark] .highlight .py { + color: #f8f8f2 +} + + +/* Name.Property */ + +:root[style*=dark] .highlight .nt { + color: #f92672 +} + + +/* Name.Tag */ + +:root[style*=dark] .highlight .nv { + color: #f8f8f2 +} + + +/* Name.Variable */ + +:root[style*=dark] .highlight .ow { + color: #f92672 +} + + +/* Operator.Word */ + +:root[style*=dark] .highlight .w { + color: #f8f8f2 +} + + +/* Text.Whitespace */ + +:root[style*=dark] .highlight .mb { + color: #ae81ff +} + + +/* Literal.Number.Bin */ + +:root[style*=dark] .highlight .mf { + color: #ae81ff +} + + +/* Literal.Number.Float */ + +:root[style*=dark] .highlight .mh { + color: #ae81ff +} + + +/* Literal.Number.Hex */ + +:root[style*=dark] .highlight .mi { + color: #ae81ff +} + + +/* Literal.Number.Integer */ + +:root[style*=dark] .highlight .mo { + color: #ae81ff +} + + +/* Literal.Number.Oct */ + +:root[style*=dark] .highlight .sa { + color: #e6db74 +} + + +/* Literal.String.Affix */ + +:root[style*=dark] .highlight .sb { + color: #e6db74 +} + + +/* Literal.String.Backtick */ + +:root[style*=dark] .highlight .sc { + color: #e6db74 +} + + +/* Literal.String.Char */ + +:root[style*=dark] .highlight .dl { + color: #e6db74 +} + + +/* Literal.String.Delimiter */ + +:root[style*=dark] .highlight .sd { + color: #e6db74 +} + + +/* Literal.String.Doc */ + +:root[style*=dark] .highlight .s2 { + color: #e6db74 +} + + +/* Literal.String.Double */ + +:root[style*=dark] .highlight .se { + color: #ae81ff +} + + +/* Literal.String.Escape */ + +:root[style*=dark] .highlight .sh { + color: #e6db74 +} + + +/* Literal.String.Heredoc */ + +:root[style*=dark] .highlight .si { + color: #e6db74 +} + + +/* Literal.String.Interpol */ + +:root[style*=dark] .highlight .sx { + color: #e6db74 +} + + +/* Literal.String.Other */ + +:root[style*=dark] .highlight .sr { + color: #e6db74 +} + + +/* Literal.String.Regex */ + +:root[style*=dark] .highlight .s1 { + color: #e6db74 +} + + +/* Literal.String.Single */ + +:root[style*=dark] .highlight .ss { + color: #e6db74 +} + + +/* Literal.String.Symbol */ + +:root[style*=dark] .highlight .bp { + color: #f8f8f2 +} + + +/* Name.Builtin.Pseudo */ + +:root[style*=dark] .highlight .fm { + color: #a6e22e +} + + +/* Name.Function.Magic */ + +:root[style*=dark] .highlight .vc { + color: #f8f8f2 +} + + +/* Name.Variable.Class */ + +:root[style*=dark] .highlight .vg { + color: #f8f8f2 +} + + +/* Name.Variable.Global */ + +:root[style*=dark] .highlight .vi { + color: #f8f8f2 +} + + +/* Name.Variable.Instance */ + +:root[style*=dark] .highlight .vm { + color: #f8f8f2 +} + + +/* Name.Variable.Magic */ + +:root[style*=dark] .highlight .il { + color: #ae81ff +} + + +/* Grammar */ + +:root[style*=dark] .railroad-diagram { + fill: white; +} + +:root[style*=dark] .railroad-diagram path { + stroke: white; +} + +:root[style*=dark] .railroad-diagram rect { + stroke: white; +} + +:root[style*=dark] .a4 .sig-name { + background-color: transparent !important; +} diff --git a/docs/_static/css/custom.css b/docs/_static/css/custom.css index 4ff53f3a7b..25ab265443 100644 --- a/docs/_static/css/custom.css +++ b/docs/_static/css/custom.css @@ -1,23 +1,185 @@ +/* ROOT DECLARATIONS */ +:root { + /* Text */ + --color-a: #2B247C; + --color-b: #672AC8; + --color-c: #5554D9; + --color-d: #9F94E8; + --color-e: #AEC0F1; + --color-f: #E6E3EC; + /* Background */ + + --white: #FAF8FF; + --black: #110C4E; + --menu-bg: #2B247C06; + --shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); + + --navHeight: 4.5rem; + --sideWidth: 300px; + --maxWidth: 80rem; + --desktopInlinePadding: 2rem; + --mobileInlinePadding: 1rem; + --currentVersionHeight: 45px; + + text-rendering: geometricPrecision; + -webkit-font-smoothing: antialiased; +} + +a, +button { + border-radius: 0; +} + +:root[style*=dark] { + --color-a: #E6E3EC !important; + --color-b: #AEC0F1 !important; + --color-c: #9F94E8 !important; + --color-d: #5554D9 !important; + --color-e: #672AC8 !important; + --color-f: #2B247C !important; + + --white: #110C4E !important; + --black: #FAF8FF !important; + --menu-bg: #E6E3EC06 !important; +} + +html, +body, +.unified-header::before, +.wy-nav-side, +.rst-versions, +code, +div, +input[type=text], +a, +.wy-grid-for-nav { + transition: background 150ms ease-in-out; +} + +html, +body, +.wy-grid-for-nav { + background-color: var(--color-f) !important; +} + +body { + font-family: "Overpass", sans-serif; +} + +a { + color: var(--color-c); +} + +a, section { + scroll-margin-top: calc(var(--navHeight) + 2rem); +} + +hr { + margin-block: 2rem; + border-color: var(--color-d) !important; +} + + +/* HEADER STYLES */ +h1 { + font-family: 'Overpass', sans-serif; + font-weight: 700; + font-size: 44px; + color: var(--color-a) !important; + line-height: 1.1; + text-wrap: balance; + margin-top: 4rem; + margin-bottom: 1.5rem; +} + +section:first-of-type h1:first-of-type { + font-family: 'Overpass mono', monospace; + font-size: 48px; + margin-top: 3rem; + margin-bottom: 5rem; +} + +h2 { + font-family: 'Overpass', sans-serif; + font-weight: 700; + font-size: 38px; + color: var(--color-a) !important; + line-height: 46px; + text-wrap: balance; + margin-top: 4rem; + margin-bottom: 1.5rem; +} + +*:not([role=navigation])>p[role=heading]>span, +h3 { + font-family: 'Overpass', sans-serif; + font-weight: 700; + font-size: 32px; + color: var(--color-a) !important; + line-height: 46px; + text-wrap: balance; + margin-top: 4rem; + margin-bottom: 1.5rem; +} + +h4 { + font-family: 'Overpass', sans-serif; + font-weight: 700; + font-size: 32px; + color: var(--color-a) !important; + line-height: 46px; + text-wrap: balance; + margin-top: 3rem; + margin-bottom: 1.5rem; +} + +h5 { + font-family: 'Overpass', sans-serif; + font-weight: 700; + font-size: 18px; + color: var(--color-a) !important; + line-height: 1.4; + text-wrap: balance; +} + +h6 { + font-family: 'Overpass', sans-serif; + font-weight: 700; + font-size: 16px; + color: var(--color-a) !important; + line-height: 1.4; + text-wrap: balance; +} + +span.pre, pre { - white-space: pre-wrap; /* css-3 */ - white-space: -moz-pre-wrap; /* Mozilla, since 1999 */ - white-space: -pre-wrap; /* Opera 4-6 */ - white-space: -o-pre-wrap; /* Opera 7 */ + /* css-3 */ + white-space: pre-wrap; + /* Mozilla, since 1999 */ + white-space: -moz-pre-wrap; + /* Opera 4-6 */ + white-space: -pre-wrap; + /* Opera 7 */ + white-space: -o-pre-wrap; word-wrap: break-word; + font-family: 'Overpass Mono', monospace; } -.wy-table-responsive table td, .wy-table-responsive table th { +small, +small * { + font-size: 12px; +} + +.wy-table-responsive table td, +.wy-table-responsive table th { white-space: normal; } + .rst-content table.docutils td { vertical-align: top; } /* links */ -.rst-content a:not(:visited) { - color: #002fa7; -} - .rst-content .highlighted { background: #eac545; } @@ -27,60 +189,638 @@ pre { background: #fafafa; } -.wy-side-nav-search img.logo { - width: 100px !important; - padding: 0; +/* project version (displayed under project logo) */ +.wy-side-nav-search>div.version { + color: var(--color-b); + margin-top: 0; + margin-bottom: 0.5rem; + text-align: start; } -.wy-side-nav-search > a { - padding: 0; +/* Link to Remix IDE shown next to code snippets */ +.rst-content p.remix-link-container { + display: block; + text-align: right; margin: 0; + line-height: 1em; } -/* project version (displayed under project logo) */ -.wy-side-nav-search .version { - color: #272525 !important; +.rst-content .remix-link-container a.remix-link { + font-size: 0.7em; + padding: 0.1em 0.5em; + background: transparent; + color: var(--color-a) !important; + border: 1px solid var(--color-a); + text-decoration: none; } -/* menu section headers */ -.wy-menu .caption { - color: #65afff !important; +.rst-content div.highlight-solidity, +.rst-content div.highlight-yul { + margin-top: 0; } -/* Link to Remix IDE shown next to code snippets */ -p.remix-link-container { +/* CUSTOMIZATION UPDATES */ + +.wy-nav-content-wrap, +.wy-nav-content { + background: transparent !important; +} + +.wy-side-nav-search { + background-color: transparent !important; + color: var(--color-a) !important; + box-shadow: 0 4 4 0 var(--color-a); + border-bottom: 1px solid var(--color-d) !important; +} + +.wy-side-nav-search svg { + color: var(--color-a) !important; +} + +.wy-nav-top { + background-color: transparent !important; + color: var(--color-a) !important; +} + +.wy-nav-top a { + color: var(--color-a) !important; +} + +.wy-breadcrumbs a.icon-home:before { + content: "Documentation"; + font-family: "Overpass", sans-serif; +} + +.rst-content table.docutils thead { + color: var(--color-a); +} + +code.docutils.literal.notranslate { + padding: 2px 4px; + font-size: 0.875em; + font-family: "Overpass Mono", monospace; + background: var(--white); + color: var(--color-c); + border: 0px; +} + +dt code.docutils.literal.notranslate { + background: none; +} + +.wy-nav-content { + color: var(--color-a); +} + +/* .rst-content a:not(:visited) { */ +/* color: var(--color-b) !important; */ +/* } */ + +.rst-content a:visited { + color: var(--color-c) !important; +} + +.rst-content a { + text-decoration: underline; +} + +.rst-content a:where(:focus, :focus-visible, :hover) { + color: var(--color-d) !important; +} + +.wy-side-scroll a { + color: var(--color-a); + background: transparent; + font-size: 1rem; + line-height: 125%; +} + +.wy-menu-vertical li.current a, +.wy-menu-vertical li.current li a, +.wy-menu-vertical li.current li a code { + border: none; + color: var(--color-a); +} + +ul.current ul, +.wy-menu-vertical li.current a:hover, +.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a, +.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a, +.wy-menu-vertical li.toctree-l4.current li.toctree-l5>a, +.wy-menu-vertical li.toctree-l5.current li.toctree-l6>a, +.wy-menu-vertical li.current { + background: var(--menu-bg) !important; +} + +.wy-menu.wy-menu-vertical>ul { + margin-bottom: 3rem; +} + +.wy-menu.wy-menu-vertical>p { + color: var(--color-c); +} + +.wy-menu-vertical li.on a, +.wy-menu-vertical li.current>a { + background: var(--menu-bg) !important; + border-bottom: 0px !important; + border-top: 0px !important; +} + +.btn { + border-radius: 0; + text-decoration: none !important; +} + +.wy-breadcrumbs-aside a, +.wy-breadcrumbs-aside a:visited, +a.fa.fa-github, +a.fa.fa-github:visited, +a.fa.fa-github:not(:visited), +a.btn.btn-neutral:visited, +a.btn.btn-neutral:not(:visited), +a.btn.btn-neutral { + background: transparent !important; + color: var(--color-a) !important; + border: 2px solid var(--color-a) !important; + text-decoration: none; +} + +.rst-content .remix-link-container a.remix-link:hover, +.wy-breadcrumbs-aside a:hover, +a.fa.fa-github:hover, +a.btn.btn-neutral:hover { + background: var(--white) !important; + color: var(--color-b) !important; + border-color: var(--color-b) !important; +} + +footer .rst-footer-buttons { + display: flex; + justify-content: center; + gap: 2rem; +} + +/** + * Customization for the unified layout + */ + +/* Site wrapper, and two children: header and rest */ +.unified-wrapper { position: relative; - right: -100%; /* Positioned next to the the top-right corner of the code block following it. */ + display: flex; + flex-direction: column; + inset: 0; + max-width: var(--maxWidth); + margin-inline: auto; +} + +/* Site header */ +.unified-header { + position: fixed; + top: 0; + inset-inline: 0; + z-index: 99999; + display: flex; + align-items: center; + box-shadow: var(--shadow); } -a.remix-link { - position: absolute; /* Remove it from normal flow not to affect the original position of the snippet. */ - top: 0.5em; - width: 3.236em; /* Size of the margin (= right-side padding in .wy-nav-content in the current theme). */ +.unified-header .inner-header { + display: flex; + margin-inline: auto; + width: 100%; + max-width: var(--maxWidth); + align-items: center; + justify-content: space-between; + padding-inline: var(--desktopInlinePadding); + padding-block: 1rem; } -a.remix-link .link-icon { - background: url("../img/solid-share-arrow.svg") no-repeat; +.unified-header::before { + content: ""; + position: absolute; + inset: 0; + opacity: 95%; + background: var(--color-f); + z-index: -1; + backdrop-filter: blur(3px); +} + +.unified-header .home-link { display: block; - width: 1.5em; - height: 1.5em; - margin: auto; + text-decoration: none; + width: 25px; + height: 40px; } -a.remix-link .link-text { - display: none; /* Visible only on hover. */ - width: 3.3em; /* Narrow enough to get two lines of text. */ - margin: auto; - text-align: center; - font-size: 0.8em; - line-height: normal; - color: black; +.unified-header .home-link:hover .solidity-logo { + transform: scale(1.1); + transition: transform 100ms ease-in-out; } -a.remix-link:hover { +.unified-header img.solidity-logo { + transform: scale(1); + transition: transform 100ms ease-in-out; + width: 100%; + height: 100%; +} + +.unified-header .nav-bar { + display: flex; + align-items: center; + justify-content: flex-end; +} + +.unified-header .nav-bar .nav-button-container { + display: flex; + align-items: center; + justify-content: center; + gap: 0.5rem; +} + +.unified-header .nav-link { + display: inline-block; + padding-inline: 8px; + padding-block: 4px; + font-size: 14px; + font-family: 'Overpass Mono', monospace; + text-decoration: none; + color: var(--color-a); + letter-spacing: -0.02em; + font-weight: 400; + box-sizing: content-box; + border-bottom: 1px solid transparent; + white-space: nowrap; +} + +.unified-header .nav-link.active { + background: var(--white); +} + +.unified-header .nav-link:hover { + color: var(--color-c); + border-bottom: 1px solid var(--color-c); +} + +/* Rest: Flex-row, with two children: side bar, and content */ +.unified-wrapper .wy-grid-for-nav { + position: relative !important; + display: flex; + margin-inline: auto; +} + +/* First child: Side bar */ +.unified-wrapper .wy-grid-for-nav nav.wy-nav-side { + position: fixed; + display: flex; + flex-direction: column; + background: var(--color-f); + color: var(--color-a); + padding-bottom: unset !important; + z-index: 10 !important; + min-height: unset !important; + width: var(--sideWidth) !important; + top: var(--navHeight); + bottom: 0; + left: auto; + overflow: auto; +} + +.unified-wrapper .wy-grid-for-nav nav.wy-nav-side .wy-side-scroll { + position: static !important; + width: unset !important; + overflow: unset !important; + height: unset !important; + padding-bottom: 2rem; +} + +.unified-wrapper .wy-grid-for-nav nav.wy-nav-side .wy-side-scroll .wy-side-nav-search { + margin: 0 !important; + width: var(--sideWidth) !important; +} + +.wy-nav-side, +.wy-side-scroll, +.wy-side-nav-search, +.my-menu { + width: 100% !important; +} + +.wy-nav-side input[type=text] { + font-family: "Overpass", sans-serif; + border-radius: 0; + border-color: var(--color-d); + background: var(--white); + box-shadow: none; + color: var(--color-a); +} + +.wy-nav-side input[type=text]::placeholder { + font-family: "Overpass", sans-serif; + color: var(--color-e); + font-size: 16px; + position: relative; + top: 4px; +} + +/* Second child: Content */ +.unified-wrapper .wy-grid-for-nav .wy-nav-content { + width: 100%; + max-width: unset !important; /* override */ + padding-inline: var(--desktopInlinePadding); + margin-inline-start: var(--sideWidth); + margin-top: var(--navHeight); +} + +.unified-wrapper .wy-grid-for-nav .wy-nav-content .rst-content { + max-width: min(70ch, calc(100vw - 2 * var(--desktopInlinePadding) - var(--sideWidth))); + margin-inline: auto; +} + +.unified-wrapper.menu-open .backdrop { opacity: 0.5; } -a.remix-link:hover .link-text { +.unified-wrapper .wy-nav-side, +.unified-wrapper .rst-versions { + left: auto; + +} + +.unified-wrapper .backdrop { + opacity: 0; + transition: opacity 200ms ease-in-out; +} + +@media (max-width: 768px) { + h2 { + margin-top: 3rem; + margin-bottom: 1rem; + } + + h3 { + margin-top: 3rem; + margin-bottom: 1rem; + } + + h4 { + margin-top: 2rem; + margin-bottom: 1rem; + } + + /* Menu closed styles */ + .unified-header .nav-link { + display: none; + } + + .unified-header .inner-header { + padding-inline: var(--mobileInlinePadding); + } + + .unified-wrapper .wy-grid-for-nav nav.wy-nav-side { + transform: translateX(-100%); + transition: transform 200ms ease-in-out; + } + + /* Menu open styles */ + .unified-wrapper.menu-open nav.wy-nav-side { + transform: translateX(0); + transition: transform 200ms ease-in-out; + } + + .unified-wrapper.menu-open .rst-versions { + position: sticky; + bottom: 0; + width: 100%; + } + + .unified-wrapper.menu-open .backdrop { + display: block; + position: fixed; + inset: 0; + opacity: 1; + transition: opacity 200ms ease-in-out; + z-index: 5; + background: #0006; + } + + a.skip-to-content { + display: none; + } + + .wy-nav-content { + margin-inline-start: 0 !important; + } + + .rst-content { + max-width: 100% !important; + } + + .wy-side-scroll { + padding-bottom: 0 !important; + } +} + +ul.search .context { + color: var(--color-a) !important; +} + +.rst-versions { + background: var(--color-f); +} + +.rst-versions.shift-up { + height: unset !important; + max-height: unset !important; + overflow-y: unset !important; +} + +.rst-content dl:not(.docutils) dt { + color: var(--color-a); + background-color: #fff8; + border-top: solid 3px #0002; + border-inline-start: solid 3px #0002; + padding: 2px 6px; +} + +.rst-versions .rst-current-version { + border-color: var(--color-d) !important; +} + +.rst-current-version *, +.rst-current-version .fa:before, +.rst-current-version .fa-element { + color: var(--color-b) !important; +} + +.rst-current-version dt, +.rst-current-version dd, +.rst-current-version dd a, +.rst-other-versions dl:last-of-type dt, +.rst-other-versions dl:last-of-type dd, +.rst-other-versions dl:last-of-type dd a { + font-size: 14px !important; +} + +.rst-other-versions { + background: var(--white) !important; + color: var(--color-a) !important; + max-height: calc(100vh - var(--navHeight) - var(--currentVersionHeight)); + overflow-y: scroll; +} + +.rst-other-versions a { + text-decoration: underline; + color: var(--color-c) !important; +} + +.rst-other-versions dt { + color: var(--color-a) !important; +} + +.rst-other-versions dl { + margin-bottom: 1.5rem !important; +} + +.rst-other-versions dl:last-of-type { + margin-top: 2rem !important; +} + +/* Bottom Search */ +.wy-nav-side input[type=text], +.rst-other-versions dl:last-of-type dd { + width: 100%; +} + +.rst-other-versions dl:last-of-type dt { + color: var(--color-b) !important; +} + +.rst-other-versions dl:last-of-type div[style*=padding], +.rst-other-versions dl dd:first-of-type a { + padding-inline-start: 0 !important; +} + +button.toctree-expand { + color: var(--black) !important; +} + +/* Light/dark color mode toggle 🌓 */ +button.color-toggle { + display: inline-flex; + appearance: none; + -webkit-box-align: center; + align-items: center; + -webkit-box-pack: center; + justify-content: center; + user-select: none; + outline: none; + height: 28px; + width: 28px; + background: none; + border: none; + padding: 6px; + margin: 6px; + transition-duration: 200ms; + transition-property: background-color, + color, + fill, + stroke, + opacity; +} + +button.color-toggle:focus-visible { + outline: 2px solid var(--color-c); + color: var(--color-c); +} + +button.color-toggle:hover { + color: var(--color-c); + background: #0002; +} + +button.color-toggle .color-toggle-icon { + width: 100%; + height: 100%; + margin: 0; + display: inline-block; + line-height: 1em; + -webkit-flex-shrink: 0; + -ms-flex-negative: 0; + flex-shrink: 0; + vertical-align: middle; + /* color: var(--color-a); */ +} + + +button.mobile-menu-button { + display: none; +} + +@media (max-width: 768px) { + nav.wy-nav-top { + display: none; + } + + button.mobile-menu-button { + display: flex; + } +} + + +.hidden { + display: none; +} + +#search-results .search li:first-child, +#search-results .search li { + border-color: var(--color-d); +} + +#search-results .search li:last-child { + border: 0px; +} + +.forum-link::after { + content: ' ↗'; + font-size: 14px; + font-family: 'Overpass Mono', monospace; +} + +.wy-breadcrumbs>li { + padding-top: 8px; +} + +.wy-breadcrumbs-aside a { + padding: 0.5rem 0.75rem; + font-size: 12px; + font-family: "'Overpass'", sans-serif; + font-weight: 700; +} + +a.skip-to-content:visited, +a.skip-to-content:not(:visited), +a.skip-to-content { display: block; + pointer-events: none; + width: fit-content; + opacity: 0; + transition: opacity 200ms ease-in-out; + padding: 2px 4px; + font-size: 14px; + margin-inline-end: auto; + margin-inline-start: 1.5rem; + color: var(--color-a); + white-space: nowrap; } + +a.skip-to-content:focus { + opacity: 1; + transition: opacity 200ms ease-in-out; +} + +#content { + scroll-margin-top: 6rem; + scroll-behavior: smooth; +} \ No newline at end of file diff --git a/docs/_static/css/dark.css b/docs/_static/css/dark.css deleted file mode 100644 index a87ff09ebe..0000000000 --- a/docs/_static/css/dark.css +++ /dev/null @@ -1,635 +0,0 @@ -/* links */ - -.rst-content a:not(:visited) { - color: #aaddff !important; -} - -/* code directives */ - -.method dt, -.class dt, -.data dt, -.attribute dt, -.function dt, -.classmethod dt, -.exception dt, -.descclassname, -.descname { - background-color: #2d2d2d !important; -} - -.rst-content dl:not(.docutils) dt { - color: #aaddff; - background-color: #2d2d2d; - border-top: solid 3px #525252; - border-left: solid 3px #525252; -} - -em.property { - color: #888888; -} - - -/* tables */ - -.rst-content table.docutils thead { - color: #ddd; -} - -.rst-content table.docutils td { - border: 0px; -} - -.rst-content table.docutils:not(.field-list) tr:nth-child(2n-1) td { - background-color: #5a5a5a; -} - -.rst-content pre { - background: none; -} - -/* inlined code highlights */ - -.xref, -.py-meth, -.rst-content a code { - color: #aaddff !important; - font-weight: normal !important; -} - -.rst-content code { - color: #eee !important; - font-weight: normal !important; -} - -code.literal { - background-color: #2d2d2d !important; - border: 1px solid #6d6d6d !important; -} - -code.docutils.literal.notranslate { - color: #ddd; -} - -/* highlight color search text */ - -.rst-content .highlighted { - background: #ff5722; - box-shadow: 0 0 0 2px #f0978b; -} - -/* notes, warnings, hints */ - -.hint .admonition-title { - background: #2aa87c !important; -} - -.warning .admonition-title { - background: #cc4444 !important; -} - -.admonition-title { - background: #3a7ca8 !important; -} - -.admonition, -.note { - background-color: #2d2d2d !important; -} - - -/* table of contents */ - -.wy-nav-content-wrap { - background-color: rgba(0, 0, 0, 0.6) !important; -} - -.sidebar { - background-color: #191919 !important; -} - -.sidebar-title { - background-color: #2b2b2b !important; -} - -.wy-menu-vertical a { - color: #ddd; -} - -.wy-menu-vertical code.docutils.literal.notranslate { - color: #404040; - background: none !important; - border: none !important; -} - -.wy-nav-content { - background: #3c3c3c; - color: #dddddd; -} - -.wy-menu-vertical li.on a, -.wy-menu-vertical li.current>a { - background: #a3a3a3; - border-bottom: 0px !important; - border-top: 0px !important; -} - -.wy-menu-vertical li.current { - background: #b3b3b3; -} - -.toc-backref { - color: grey !important; -} - -.highlight .hll { - background-color: #49483e -} - -.highlight { - background: #222; - color: #f8f8f2 -} - -.highlight .c { - color: #888 -} - - -/* Comment */ - -.highlight .err { - color: #960050; - background-color: #1e0010 -} - - -/* Error */ - -.highlight .k { - color: #66d9ef -} - - -/* Keyword */ - -.highlight .l { - color: #ae81ff -} - - -/* Literal */ - -.highlight .n { - color: #f8f8f2 -} - - -/* Name */ - -.highlight .o { - color: #f92672 -} - - -/* Operator */ - -.highlight .p { - color: #f8f8f2 -} - - -/* Punctuation */ - -.highlight .ch { - color: #888 -} - - -/* Comment.Hashbang */ - -.highlight .cm { - color: #888 -} - - -/* Comment.Multiline */ - -.highlight .cp { - color: #888 -} - - -/* Comment.Preproc */ - -.highlight .cpf { - color: #888 -} - - -/* Comment.PreprocFile */ - -.highlight .c1 { - color: #888 -} - - -/* Comment.Single */ - -.highlight .cs { - color: #888 -} - - -/* Comment.Special */ - -.highlight .gd { - color: #f92672 -} - - -/* Generic.Deleted */ - -.highlight .ge { - font-style: italic -} - - -/* Generic.Emph */ - -.highlight .gi { - color: #a6e22e -} - - -/* Generic.Inserted */ - -.highlight .gs { - font-weight: bold -} - - -/* Generic.Strong */ - -.highlight .gu { - color: #888 -} - - -/* Generic.Subheading */ - -.highlight .kc { - color: #66d9ef -} - - -/* Keyword.Constant */ - -.highlight .kd { - color: #66d9ef -} - - -/* Keyword.Declaration */ - -.highlight .kn { - color: #f92672 -} - - -/* Keyword.Namespace */ - -.highlight .kp { - color: #66d9ef -} - - -/* Keyword.Pseudo */ - -.highlight .kr { - color: #66d9ef -} - - -/* Keyword.Reserved */ - -.highlight .kt { - color: #66d9ef -} - - -/* Keyword.Type */ - -.highlight .ld { - color: #e6db74 -} - - -/* Literal.Date */ - -.highlight .m { - color: #ae81ff -} - - -/* Literal.Number */ - -.highlight .s { - color: #e6db74 -} - - -/* Literal.String */ - -.highlight .na { - color: #a6e22e -} - - -/* Name.Attribute */ - -.highlight .nb { - color: #f8f8f2 -} - - -/* Name.Builtin */ - -.highlight .nc { - color: #a6e22e -} - - -/* Name.Class */ - -.highlight .no { - color: #66d9ef -} - - -/* Name.Constant */ - -.highlight .nd { - color: #a6e22e -} - - -/* Name.Decorator */ - -.highlight .ni { - color: #f8f8f2 -} - - -/* Name.Entity */ - -.highlight .ne { - color: #a6e22e -} - - -/* Name.Exception */ - -.highlight .nf { - color: #a6e22e -} - - -/* Name.Function */ - -.highlight .nl { - color: #f8f8f2 -} - - -/* Name.Label */ - -.highlight .nn { - color: #f8f8f2 -} - - -/* Name.Namespace */ - -.highlight .nx { - color: #a6e22e -} - - -/* Name.Other */ - -.highlight .py { - color: #f8f8f2 -} - - -/* Name.Property */ - -.highlight .nt { - color: #f92672 -} - - -/* Name.Tag */ - -.highlight .nv { - color: #f8f8f2 -} - - -/* Name.Variable */ - -.highlight .ow { - color: #f92672 -} - - -/* Operator.Word */ - -.highlight .w { - color: #f8f8f2 -} - - -/* Text.Whitespace */ - -.highlight .mb { - color: #ae81ff -} - - -/* Literal.Number.Bin */ - -.highlight .mf { - color: #ae81ff -} - - -/* Literal.Number.Float */ - -.highlight .mh { - color: #ae81ff -} - - -/* Literal.Number.Hex */ - -.highlight .mi { - color: #ae81ff -} - - -/* Literal.Number.Integer */ - -.highlight .mo { - color: #ae81ff -} - - -/* Literal.Number.Oct */ - -.highlight .sa { - color: #e6db74 -} - - -/* Literal.String.Affix */ - -.highlight .sb { - color: #e6db74 -} - - -/* Literal.String.Backtick */ - -.highlight .sc { - color: #e6db74 -} - - -/* Literal.String.Char */ - -.highlight .dl { - color: #e6db74 -} - - -/* Literal.String.Delimiter */ - -.highlight .sd { - color: #e6db74 -} - - -/* Literal.String.Doc */ - -.highlight .s2 { - color: #e6db74 -} - - -/* Literal.String.Double */ - -.highlight .se { - color: #ae81ff -} - - -/* Literal.String.Escape */ - -.highlight .sh { - color: #e6db74 -} - - -/* Literal.String.Heredoc */ - -.highlight .si { - color: #e6db74 -} - - -/* Literal.String.Interpol */ - -.highlight .sx { - color: #e6db74 -} - - -/* Literal.String.Other */ - -.highlight .sr { - color: #e6db74 -} - - -/* Literal.String.Regex */ - -.highlight .s1 { - color: #e6db74 -} - - -/* Literal.String.Single */ - -.highlight .ss { - color: #e6db74 -} - - -/* Literal.String.Symbol */ - -.highlight .bp { - color: #f8f8f2 -} - - -/* Name.Builtin.Pseudo */ - -.highlight .fm { - color: #a6e22e -} - - -/* Name.Function.Magic */ - -.highlight .vc { - color: #f8f8f2 -} - - -/* Name.Variable.Class */ - -.highlight .vg { - color: #f8f8f2 -} - - -/* Name.Variable.Global */ - -.highlight .vi { - color: #f8f8f2 -} - - -/* Name.Variable.Instance */ - -.highlight .vm { - color: #f8f8f2 -} - - -/* Name.Variable.Magic */ - -.highlight .il { - color: #ae81ff -} - - -/* Literal.Number.Integer.Long */ - - -/* Link to Remix IDE shown over code snippets */ -a.remix-link { - filter: invert(1); /* The icon is black. In dark mode we want it white. */ -} diff --git a/docs/_static/css/fonts.css b/docs/_static/css/fonts.css new file mode 100644 index 0000000000..1a987a6da1 --- /dev/null +++ b/docs/_static/css/fonts.css @@ -0,0 +1,2 @@ +@import url("https://fonts.cdnfonts.com/css/overpass"); +@import url("https://fonts.cdnfonts.com/css/overpass-mono"); \ No newline at end of file diff --git a/docs/_static/css/pygments.css b/docs/_static/css/pygments.css new file mode 100644 index 0000000000..0e640681de --- /dev/null +++ b/docs/_static/css/pygments.css @@ -0,0 +1,399 @@ +pre { + line-height: 125%; +} + +td.linenos .normal { + color: inherit; + background-color: transparent; + padding-left: 5px; + padding-right: 5px; +} + +span.linenos { + color: inherit; + background-color: transparent; + padding-left: 5px; + padding-right: 5px; +} + +td.linenos .special { + color: #000000; + background-color: #ffffc0; + padding-left: 5px; + padding-right: 5px; +} + +span.linenos.special { + color: #000000; + background-color: #ffffc0; + padding-left: 5px; + padding-right: 5px; +} + +.highlight .hll { + background-color: #ffffcc +} + +.highlight { + background: #eeffcc; +} + +.highlight .c { + color: #408090; + font-style: italic +} + +/* Comment */ +.highlight .err { + border: 1px solid #FF0000 +} + +/* Error */ +.highlight .k { + color: #007020; + font-weight: bold +} + +/* Keyword */ +.highlight .o { + color: #666666 +} + +/* Operator */ +.highlight .ch { + color: #408090; + font-style: italic +} + +/* Comment.Hashbang */ +.highlight .cm { + color: #408090; + font-style: italic +} + +/* Comment.Multiline */ +.highlight .cp { + color: #007020 +} + +/* Comment.Preproc */ +.highlight .cpf { + color: #408090; + font-style: italic +} + +/* Comment.PreprocFile */ +.highlight .c1 { + color: #408090; + font-style: italic +} + +/* Comment.Single */ +.highlight .cs { + color: #408090; + background-color: #fff0f0 +} + +/* Comment.Special */ +.highlight .gd { + color: #A00000 +} + +/* Generic.Deleted */ +.highlight .ge { + font-style: italic +} + +/* Generic.Emph */ +.highlight .gr { + color: #FF0000 +} + +/* Generic.Error */ +.highlight .gh { + color: #000080; + font-weight: bold +} + +/* Generic.Heading */ +.highlight .gi { + color: #00A000 +} + +/* Generic.Inserted */ +.highlight .go { + color: #333333 +} + +/* Generic.Output */ +.highlight .gp { + color: #c65d09; + font-weight: bold +} + +/* Generic.Prompt */ +.highlight .gs { + font-weight: bold +} + +/* Generic.Strong */ +.highlight .gu { + color: #800080; + font-weight: bold +} + +/* Generic.Subheading */ +.highlight .gt { + color: #0044DD +} + +/* Generic.Traceback */ +.highlight .kc { + color: #007020; + font-weight: bold +} + +/* Keyword.Constant */ +.highlight .kd { + color: #007020; + font-weight: bold +} + +/* Keyword.Declaration */ +.highlight .kn { + color: #007020; + font-weight: bold +} + +/* Keyword.Namespace */ +.highlight .kp { + color: #007020 +} + +/* Keyword.Pseudo */ +.highlight .kr { + color: #007020; + font-weight: bold +} + +/* Keyword.Reserved */ +.highlight .kt { + color: #902000 +} + +/* Keyword.Type */ +.highlight .m { + color: #208050 +} + +/* Literal.Number */ +.highlight .s { + color: #4070a0 +} + +/* Literal.String */ +.highlight .na { + color: #4070a0 +} + +/* Name.Attribute */ +.highlight .nb { + color: #007020 +} + +/* Name.Builtin */ +.highlight .nc { + color: #0e84b5; + font-weight: bold +} + +/* Name.Class */ +.highlight .no { + color: #60add5 +} + +/* Name.Constant */ +.highlight .nd { + color: #555555; + font-weight: bold +} + +/* Name.Decorator */ +.highlight .ni { + color: #d55537; + font-weight: bold +} + +/* Name.Entity */ +.highlight .ne { + color: #007020 +} + +/* Name.Exception */ +.highlight .nf { + color: #06287e +} + +/* Name.Function */ +.highlight .nl { + color: #002070; + font-weight: bold +} + +/* Name.Label */ +.highlight .nn { + color: #0e84b5; + font-weight: bold +} + +/* Name.Namespace */ +.highlight .nt { + color: #062873; + font-weight: bold +} + +/* Name.Tag */ +.highlight .nv { + color: #bb60d5 +} + +/* Name.Variable */ +.highlight .ow { + color: #007020; + font-weight: bold +} + +/* Operator.Word */ +.highlight .w { + color: #bbbbbb +} + +/* Text.Whitespace */ +.highlight .mb { + color: #208050 +} + +/* Literal.Number.Bin */ +.highlight .mf { + color: #208050 +} + +/* Literal.Number.Float */ +.highlight .mh { + color: #208050 +} + +/* Literal.Number.Hex */ +.highlight .mi { + color: #208050 +} + +/* Literal.Number.Integer */ +.highlight .mo { + color: #208050 +} + +/* Literal.Number.Oct */ +.highlight .sa { + color: #4070a0 +} + +/* Literal.String.Affix */ +.highlight .sb { + color: #4070a0 +} + +/* Literal.String.Backtick */ +.highlight .sc { + color: #4070a0 +} + +/* Literal.String.Char */ +.highlight .dl { + color: #4070a0 +} + +/* Literal.String.Delimiter */ +.highlight .sd { + color: #4070a0; + font-style: italic +} + +/* Literal.String.Doc */ +.highlight .s2 { + color: #4070a0 +} + +/* Literal.String.Double */ +.highlight .se { + color: #4070a0; + font-weight: bold +} + +/* Literal.String.Escape */ +.highlight .sh { + color: #4070a0 +} + +/* Literal.String.Heredoc */ +.highlight .si { + color: #70a0d0; + font-style: italic +} + +/* Literal.String.Interpol */ +.highlight .sx { + color: #c65d09 +} + +/* Literal.String.Other */ +.highlight .sr { + color: #235388 +} + +/* Literal.String.Regex */ +.highlight .s1 { + color: #4070a0 +} + +/* Literal.String.Single */ +.highlight .ss { + color: #517918 +} + +/* Literal.String.Symbol */ +.highlight .bp { + color: #007020 +} + +/* Name.Builtin.Pseudo */ +.highlight .fm { + color: #06287e +} + +/* Name.Function.Magic */ +.highlight .vc { + color: #bb60d5 +} + +/* Name.Variable.Class */ +.highlight .vg { + color: #bb60d5 +} + +/* Name.Variable.Global */ +.highlight .vi { + color: #bb60d5 +} + +/* Name.Variable.Instance */ +.highlight .vm { + color: #bb60d5 +} + +/* Name.Variable.Magic */ +.highlight .il { + color: #208050 +} + +/* Literal.Number.Integer.Long */ \ No newline at end of file diff --git a/docs/_static/css/toggle.css b/docs/_static/css/toggle.css index add134f6c2..d525ab62a7 100644 --- a/docs/_static/css/toggle.css +++ b/docs/_static/css/toggle.css @@ -9,6 +9,13 @@ input[type=checkbox] { padding: 10px; display: flex; justify-content: space-between; + background-color: var(--color-f); + border-top: 1px solid var(--color-c); +} + +.fa-caret-down, +.fa-book { + color: var(--color-a) !important; } .rst-versions .rst-current-version .fa-book, @@ -76,8 +83,11 @@ html.transition *:after { transition-delay: 0 !important; } -nav.wy-nav-side { - /* The default padding of 2em is too small and the "Keyword Index" link gets obscured - * by the version toggle. */ - padding-bottom: 3em; +.wy-menu-vertical a:hover { + background-color: #0002; } + +body { + font-weight: 300; + letter-spacing: 0.5px; +} \ No newline at end of file diff --git a/docs/_static/img/favicon.ico b/docs/_static/img/favicon.ico new file mode 100644 index 0000000000..a2b8f877a3 Binary files /dev/null and b/docs/_static/img/favicon.ico differ diff --git a/docs/_static/img/favicon.png b/docs/_static/img/favicon.png new file mode 100644 index 0000000000..3991d87e98 Binary files /dev/null and b/docs/_static/img/favicon.png differ diff --git a/docs/_static/img/hamburger-dark.svg b/docs/_static/img/hamburger-dark.svg new file mode 100644 index 0000000000..26d9fed9df --- /dev/null +++ b/docs/_static/img/hamburger-dark.svg @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/docs/_static/img/hamburger-light.svg b/docs/_static/img/hamburger-light.svg new file mode 100644 index 0000000000..d5d0d0aed2 --- /dev/null +++ b/docs/_static/img/hamburger-light.svg @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/docs/_static/img/logo-dark.svg b/docs/_static/img/logo-dark.svg new file mode 100644 index 0000000000..92a12a9fed --- /dev/null +++ b/docs/_static/img/logo-dark.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/docs/_static/img/logo.svg b/docs/_static/img/logo.svg new file mode 100644 index 0000000000..19391843b4 --- /dev/null +++ b/docs/_static/img/logo.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/docs/_static/img/moon.svg b/docs/_static/img/moon.svg new file mode 100644 index 0000000000..607dc1b47f --- /dev/null +++ b/docs/_static/img/moon.svg @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/docs/_static/img/sun.svg b/docs/_static/img/sun.svg new file mode 100644 index 0000000000..f86fd22b2d --- /dev/null +++ b/docs/_static/img/sun.svg @@ -0,0 +1,13 @@ + \ No newline at end of file diff --git a/docs/_static/js/constants.js b/docs/_static/js/constants.js new file mode 100644 index 0000000000..67fa16cdb0 --- /dev/null +++ b/docs/_static/js/constants.js @@ -0,0 +1,38 @@ +// Site URL +const SITE_URL = "https://docs.soliditylang.org" +const { origin, pathname } = location; +const pathSplit = pathname.split("/"); +const rootPath = origin.includes(SITE_URL) && pathSplit.length > 3 ? pathSplit.splice(1, 2).join("/") : '' +const ROOT_URL = `${origin}/${rootPath}`; + +// Color mode constants +const [DARK, LIGHT] = ["dark", "light"]; +const LIGHT_LOGO_PATH = `${ROOT_URL}/_static/img/logo.svg`; +const DARK_LOGO_PATH = `${ROOT_URL}/_static/img/logo-dark.svg`; +const SUN_ICON_PATH = `${ROOT_URL}/_static/img/sun.svg`; +const MOON_ICON_PATH = `${ROOT_URL}/_static/img/moon.svg`; +const LIGHT_HAMBURGER_PATH = `${ROOT_URL}/_static/img/hamburger-light.svg`; +const DARK_HAMBURGER_PATH = `${ROOT_URL}/_static/img/hamburger-dark.svg`; +const COLOR_TOGGLE_ICON_CLASS = "color-toggle-icon"; +const SOLIDITY_LOGO_CLASS = "solidity-logo"; +const LS_COLOR_SCHEME = "color-scheme"; + +// Solidity navigation constants +const SOLIDITY_HOME_URL = "https://soliditylang.org"; +const BLOG_URL = `${SOLIDITY_HOME_URL}/blog`; +const DOCS_URL = "/"; +const USE_CASES_PATH = `${SOLIDITY_HOME_URL}/use-cases`; +const CONTRIBUTE_PATH = `/en/latest/contributing.html`; +const ABOUT_PATH = `${SOLIDITY_HOME_URL}/about`; +const FORUM_URL = "https://forum.soliditylang.org/"; +const NAV_LINKS = [ + { name: "Blog", href: BLOG_URL }, + { name: "Documentation", href: DOCS_URL }, + { name: "Use cases", href: USE_CASES_PATH }, + { name: "Contribute", href: CONTRIBUTE_PATH }, + { name: "About", href: ABOUT_PATH }, + { name: "Forum", href: FORUM_URL }, +]; + +const MOBILE_MENU_TOGGLE_CLASS = "shift"; +const WRAPPER_CLASS = "unified-wrapper"; diff --git a/docs/_static/js/initialize.js b/docs/_static/js/initialize.js new file mode 100644 index 0000000000..1c3be8aadd --- /dev/null +++ b/docs/_static/js/initialize.js @@ -0,0 +1,265 @@ +const getLogoSrc = (isDark) => (isDark ? DARK_LOGO_PATH : LIGHT_LOGO_PATH); + +const getModeIconSrc = (isDark) => (isDark ? SUN_ICON_PATH : MOON_ICON_PATH); + +const getMenuIconSrc = (isDark) => + isDark ? DARK_HAMBURGER_PATH : LIGHT_HAMBURGER_PATH; + +function addFooterNote() { + const contentInfo = document.querySelector("div[role=contentinfo]"); + const footerNote = document.createElement("p"); + footerNote.classList.add("footer-note"); + footerNote.innerHTML = + 'Customized with ❤️ by the ethereum.org team.'; + contentInfo.parentNode.insertBefore(footerNote, contentInfo.nextSibling); +} + +function rearrangeDom() { + const bodyDivs = document.querySelectorAll("body>div"); + bodyDivs.forEach((div) => div.remove()); + const wrapperDiv = document.createElement("div"); + wrapperDiv.classList.add(WRAPPER_CLASS); + bodyDivs.forEach((div) => wrapperDiv.appendChild(div)); + document.body.prepend(wrapperDiv); + + const rstVersions = document.querySelector(".rst-versions"); + const wyNavSide = document.querySelector("nav.wy-nav-side"); + // NOTE: Since RTD migration to addons, `.rst-versions` is no longer present in the DOM. + // The following code is kept for compatibility with older versions. + // See: https://github.com/readthedocs/readthedocs.org/issues/11474 + if (rstVersions && wyNavSide) { + rstVersions.remove(); + wyNavSide.appendChild(rstVersions); + } + const backdrop = document.createElement("div"); + backdrop.classList.add("backdrop"); + wrapperDiv.appendChild(backdrop); + + const content = document.querySelector(".wy-nav-content"); + if (content) { + content.id = "content"; + document.querySelector("section.wy-nav-content-wrap")?.remove(); + document.querySelector(".wy-grid-for-nav")?.appendChild(content); + } +} + +function buildHeader() { + const isDarkMode = localStorage.getItem(LS_COLOR_SCHEME) == DARK; + + const header = document.createElement("div"); + header.classList.add("unified-header"); + document.querySelector(`.${WRAPPER_CLASS}`).prepend(header); + + const innerHeader = document.createElement("div"); + innerHeader.classList.add("inner-header"); + header.appendChild(innerHeader); + + const homeLink = document.createElement("a"); + homeLink.classList.add("home-link"); + homeLink.href = SOLIDITY_HOME_URL; + homeLink.ariaLabel = "Solidity home"; + innerHeader.appendChild(homeLink); + + const logo = document.createElement("img"); + logo.classList.add(SOLIDITY_LOGO_CLASS); + logo.src = getLogoSrc(isDarkMode); + logo.alt = "Solidity logo"; + homeLink.appendChild(logo); + + const skipToContent = document.createElement("a"); + skipToContent.classList.add("skip-to-content"); + skipToContent.href = "#content"; + skipToContent.innerText = "{skip to content}"; + innerHeader.appendChild(skipToContent); + + const navBar = document.createElement("nav"); + navBar.classList.add("nav-bar"); + innerHeader.appendChild(navBar); + + const linkElements = NAV_LINKS.map(({ name, href }) => { + const link = document.createElement("a"); + link.classList.add("nav-link"); + link.setAttribute("key", name); + link.setAttribute("href", href); + link.setAttribute("aria-label", name); + if (href === FORUM_URL) { + link.classList.add("forum-link"); + link.setAttribute("target", "_blank"); + link.setAttribute("rel", "noopener noreferrer"); + } + link.innerText = name; + return link; + }); + linkElements.forEach((link) => navBar.appendChild(link)); + + // Flex wrapper for color mode and mobile menu buttons + const navButtonContainer = document.createElement("div"); + navButtonContainer.classList.add("nav-button-container"); + navBar.appendChild(navButtonContainer); + + // Build color toggle + const toggleIcon = document.createElement("img"); + toggleIcon.classList.add(COLOR_TOGGLE_ICON_CLASS); + toggleIcon.src = getModeIconSrc(isDarkMode); + toggleIcon.alt = "Color mode toggle icon"; + toggleIcon.setAttribute("aria-hidden", "true"); + toggleIcon.setAttribute("key", "toggle icon"); + const colorModeButton = document.createElement("button"); + colorModeButton.classList.add("color-toggle"); + colorModeButton.setAttribute("type", "button"); + colorModeButton.setAttribute("aria-label", "Toggle light dark mode"); + colorModeButton.setAttribute("key", "color mode button"); + colorModeButton.addEventListener("click", toggleColorMode); + colorModeButton.appendChild(toggleIcon); + navButtonContainer.appendChild(colorModeButton); + + // Build mobile hamburger menu + const menuIcon = document.createElement("img"); + menuIcon.classList.add(COLOR_TOGGLE_ICON_CLASS); + menuIcon.src = getMenuIconSrc(isDarkMode); + menuIcon.alt = "Toggle menu"; + menuIcon.setAttribute("aria-hidden", "true"); + menuIcon.setAttribute("key", "menu icon"); + const menuButton = document.createElement("button"); + menuButton.classList.add("color-toggle"); + menuButton.classList.add("mobile-menu-button"); + menuButton.setAttribute("type", "button"); + menuButton.setAttribute("aria-label", "Toggle menu"); + menuButton.setAttribute("key", "menu button"); + menuButton.addEventListener("click", toggleMenu); + menuButton.appendChild(menuIcon); + navButtonContainer.appendChild(menuButton); +} + +const updateActiveNavLink = () => { + const navLinks = document.querySelectorAll(".unified-header .nav-link"); + navLinks.forEach((link) => { + const href = link.getAttribute("href"); + if (document.documentURI.includes("contributing.html")) { + link.classList[href.includes("contributing.html") ? "add" : "remove"]( + "active" + ); + } else { + link.classList[document.documentURI.includes(href) ? "add" : "remove"]( + "active" + ); + } + }); +}; + +document.addEventListener("locationchange", updateActiveNavLink); + +function updateGitHubEditPath() { + // Replaces the version number in the GitHub edit path with "develop" if it exists. + // This is to ensure that the edit path always points to the `develop` branch instead of the specific version branch. + // Note that it will fail silently if the anchor element is not found, i.e. the page is not editable or + // if the sphinx_rtd_theme is updated to a version that changes the anchor element. + // See: https://github.com/readthedocs/sphinx_rtd_theme/blob/a1c2147b17cbf0e57b7d7a6450ad4d9a5ff362cf/sphinx_rtd_theme/breadcrumbs.html#L35 + // TODO: We should consider a more robust way to handle this in the future. + const gitHubEditAnchor = document.querySelector(".wy-breadcrumbs-aside > a"); + if (!gitHubEditAnchor) return; + + const url = new URL(gitHubEditAnchor.href); + const split = url.pathname.split("/"); + const versionIndex = split.indexOf("blob") + 1; + split[versionIndex] = "develop"; + url.pathname = split.join("/"); + gitHubEditAnchor.setAttribute("href", url.toString()); + gitHubEditAnchor.setAttribute("target", "_blank"); + gitHubEditAnchor.setAttribute("rel", "noopener noreferrer"); +} + +function initialize() { + // Rearrange DOM elements for styling + rearrangeDom(); + + // Check localStorage for existing color scheme preference + var prefersDark = localStorage.getItem(LS_COLOR_SCHEME) == DARK; + // Check link for search param "color"... it may be "light" or "dark" + var urlParams = new URLSearchParams(window.location.search); + if (urlParams.size > 0) { + // This is used for color mode continuity between the main Solidity Lang site and the docs + var colorSchemeParam = urlParams.get("color"); + // If present, overwrite prefersDark accordingly + if (colorSchemeParam) { + prefersDark = colorSchemeParam == DARK; + } + + // Remove "color" search param from URL + const { location, title } = document; + const { pathname, origin, search, hash } = location; + const newSearchParams = new URLSearchParams(search); + newSearchParams.delete("color"); + const sanitizedSearch = + newSearchParams.size < 1 ? "" : "?" + newSearchParams.toString(); + window.history.replaceState( + origin, + title, + pathname + sanitizedSearch + hash + ); + } + + // In case none existed, establish localStorage color scheme preference + var mode = prefersDark ? DARK : LIGHT; + localStorage.setItem(LS_COLOR_SCHEME, mode); + + // Select the root element and set the style attribute to denote color-scheme attribute + document + .querySelector(":root") + .setAttribute("style", `--color-scheme: ${mode}`); + + // NOTE: Since RTD migration to addons, the elements below are no longer present in the DOM. + // The following code is kept for compatibility with older versions. + // Remove old input and RTD logo anchor element + document.querySelector("input[name=mode]")?.remove(); + document.querySelector("label[for=switch]")?.remove(); + document.querySelector(".wy-side-nav-search > a")?.remove(); + + // Add footer note + addFooterNote(); + + // Build header + buildHeader(); + + // Close menu + toggleMenu({ force: false }); + + // Update active nav link + updateActiveNavLink(); + + // Update GitHub edit path to direct to `develop` branch + updateGitHubEditPath(); +} + +document.addEventListener("DOMContentLoaded", initialize); + +const handleClick = (e) => { + if (e.target.closest(".backdrop")) { + toggleMenu({ force: false }); + } + + if (e.target.closest("a")) { + const target = e.target.closest("a"); + const href = target.getAttribute("href"); + if (href.includes(SOLIDITY_HOME_URL)) { + const url = new URL(href); + const params = new URLSearchParams(url.search); + params.set("color", localStorage.getItem(LS_COLOR_SCHEME)); + url.search = params.toString(); + target.setAttribute("href", url.toString()); + } + } +}; +document.addEventListener("click", handleClick); + +const handleKeyDown = (e) => { + if (e.metaKey && e.key === "k") { + document.querySelector("#rtd-search-form input")?.focus(); + } else if (e.key === "Escape") { + toggleMenu({ force: false }); + } + if (e.metaKey && e.code === "Backslash") { + toggleColorMode(); + } +}; +document.addEventListener("keydown", handleKeyDown); diff --git a/docs/_static/js/toggle.js b/docs/_static/js/toggle.js index 780ea9ee71..6ea2dd1f80 100644 --- a/docs/_static/js/toggle.js +++ b/docs/_static/js/toggle.js @@ -1,39 +1,47 @@ -document.addEventListener('DOMContentLoaded', function() { +function toggleColorMode() { + // Check localStorage for previous color scheme preference, assign the opposite + var newMode = localStorage.getItem(LS_COLOR_SCHEME) == DARK ? LIGHT : DARK; - function toggleCssMode(isDay) { - var mode = (isDay ? "Day" : "Night"); - localStorage.setItem("css-mode", mode); + // Update localStorage with new color scheme preference + localStorage.setItem(LS_COLOR_SCHEME, newMode); - var url_root = DOCUMENTATION_OPTIONS.URL_ROOT == "./" ? "" : DOCUMENTATION_OPTIONS.URL_ROOT; - var daysheet = $(`link[href="${url_root}_static/pygments.css"]`)[0].sheet; - daysheet.disabled = !isDay; + // Update the root element with the new color scheme preference + document + .querySelector(":root") + .setAttribute("style", `--color-scheme: ${newMode}`); - var nightsheet = $(`link[href="${url_root}_static/css/dark.css"]`)[0]; - if (!isDay && nightsheet === undefined) { - var element = document.createElement("link"); - element.setAttribute("rel", "stylesheet"); - element.setAttribute("type", "text/css"); - element.setAttribute("href", `${url_root}_static/css/dark.css`); - document.getElementsByTagName("head")[0].appendChild(element); - return; - } - if (nightsheet !== undefined) { - nightsheet.sheet.disabled = isDay; - } - } - - var initial = localStorage.getItem("css-mode") != "Night"; - var checkbox = document.querySelector('input[name=mode]'); + // Update logo + document + .querySelector(`img.${SOLIDITY_LOGO_CLASS}`) + .setAttribute("src", newMode === LIGHT ? LIGHT_LOGO_PATH : DARK_LOGO_PATH); - toggleCssMode(initial); - checkbox.checked = initial; + // Update color mode toggle icon + document + .querySelector(`img.${COLOR_TOGGLE_ICON_CLASS}`) + .setAttribute("src", newMode === LIGHT ? MOON_ICON_PATH : SUN_ICON_PATH); - checkbox.addEventListener('change', function() { - document.documentElement.classList.add('transition'); - window.setTimeout(() => { - document.documentElement.classList.remove('transition'); - }, 1000) - toggleCssMode(this.checked); - }) + // Update hamburger menu icon color + document + .querySelector("button.mobile-menu-button img") + .setAttribute( + "src", + newMode === LIGHT ? LIGHT_HAMBURGER_PATH : DARK_HAMBURGER_PATH + ); +} -}); \ No newline at end of file +function toggleMenu(options = {}) { + const handleClassToggle = ({ classList }, className) => { + if (typeof options.force !== "undefined") { + classList.toggle(className, options.force); + } else { + classList.toggle(className); + } + }; + document + .querySelectorAll('[data-toggle="rst-versions"]') + .forEach((e) => handleClassToggle(e, MOBILE_MENU_TOGGLE_CLASS)); + document + .querySelectorAll('[data-toggle="wy-nav-shift"]') + .forEach((e) => handleClassToggle(e, MOBILE_MENU_TOGGLE_CLASS)); + handleClassToggle(document.querySelector(`.${WRAPPER_CLASS}`), "menu-open"); +} diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst index 7906d75ffe..91da5127c5 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -16,7 +16,7 @@ as described in this specification. The encoding is not self describing and thus We assume that the interface functions of a contract are strongly typed, known at compilation time and static. We assume that all contracts will have the interface definitions of any contracts they call available at compile-time. -This specification does not address contracts whose interface is dynamic or otherwise known only at run-time. +This specification does not address contracts whose interface is dynamic or otherwise known only at run-time. Also, the ABI specification for libraries is :ref:`slightly different `. .. _abi_function_selector: .. index:: ! selector; of a function @@ -47,6 +47,8 @@ without the four bytes specifying the function. Types ===== +Note that the library ABIs can take types different than below e.g. for non-storage structs. See :ref:`library selectors ` for details. + The following elementary types exist: - ``uint``: unsigned integer type of ``M`` bits, ``0 < M <= 256``, ``M % 8 == 0``. e.g. ``uint32``, ``uint8``, ``uint256``. @@ -252,7 +254,21 @@ Given the contract: } -Thus, for our ``Foo`` example if we wanted to call ``baz`` with the parameters ``69`` and +Thus, for our ``Foo`` example, if we wanted to call ``bar`` with the argument ``["abc", "def"]``, we would pass 68 bytes total, broken down into: + +- ``0xfce353f6``: the Method ID. This is derived from the signature ``bar(bytes3[2])``. +- ``0x6162630000000000000000000000000000000000000000000000000000000000``: the first part of the first + parameter, a ``bytes3`` value ``"abc"`` (left-aligned). +- ``0x6465660000000000000000000000000000000000000000000000000000000000``: the second part of the first + parameter, a ``bytes3`` value ``"def"`` (left-aligned). + +In total: + +.. code-block:: none + + 0xfce353f661626300000000000000000000000000000000000000000000000000000000006465660000000000000000000000000000000000000000000000000000000000 + +If we wanted to call ``baz`` with the parameters ``69`` and ``true``, we would pass 68 bytes total, which can be broken down into: - ``0xcdcd77c0``: the Method ID. This is derived as the first 4 bytes of the Keccak hash of @@ -271,20 +287,6 @@ In total: It returns a single ``bool``. If, for example, it were to return ``false``, its output would be the single byte array ``0x0000000000000000000000000000000000000000000000000000000000000000``, a single bool. -If we wanted to call ``bar`` with the argument ``["abc", "def"]``, we would pass 68 bytes total, broken down into: - -- ``0xfce353f6``: the Method ID. This is derived from the signature ``bar(bytes3[2])``. -- ``0x6162630000000000000000000000000000000000000000000000000000000000``: the first part of the first - parameter, a ``bytes3`` value ``"abc"`` (left-aligned). -- ``0x6465660000000000000000000000000000000000000000000000000000000000``: the second part of the first - parameter, a ``bytes3`` value ``"def"`` (left-aligned). - -In total: - -.. code-block:: none - - 0xfce353f661626300000000000000000000000000000000000000000000000000000000006465660000000000000000000000000000000000000000000000000000000000 - If we wanted to call ``sam`` with the arguments ``"dave"``, ``true`` and ``[1,2,3]``, we would pass 292 bytes total, broken down into: @@ -311,7 +313,7 @@ Use of Dynamic Types A call to a function with the signature ``f(uint256,uint32[],bytes10,bytes)`` with values ``(0x123, [0x456, 0x789], "1234567890", "Hello, world!")`` is encoded in the following way: -We take the first four bytes of ``sha3("f(uint256,uint32[],bytes10,bytes)")``, i.e. ``0x8be65246``. +We take the first four bytes of ``keccak("f(uint256,uint32[],bytes10,bytes)")``, i.e. ``0x8be65246``. Then we encode the head parts of all four arguments. For the static types ``uint256`` and ``bytes10``, these are directly the values we want to pass, whereas for the dynamic types ``uint32[]`` and ``bytes``, we use the offset in bytes to the start of their data area, measured from the start of the value @@ -563,7 +565,7 @@ A function description is a JSON object with the fields: blockchain state `), ``view`` (:ref:`specified to not modify the blockchain state `), ``nonpayable`` (function does not accept Ether - the default) and ``payable`` (function accepts Ether). -Constructor and fallback function never have ``name`` or ``outputs``. Fallback function doesn't have ``inputs`` either. +Constructor, receive, and fallback never have ``name`` or ``outputs``. Receive and fallback do not have ``inputs`` either. .. note:: Sending non-zero Ether to non-payable function will revert the transaction. @@ -581,7 +583,7 @@ An event description is a JSON object with fairly similar fields: * ``name``: the name of the parameter. * ``type``: the canonical type of the parameter (more below). * ``components``: used for tuple types (more below). - * ``indexed``: ``true`` if the field is part of the log's topics, ``false`` if it one of the log's data segment. + * ``indexed``: ``true`` if the field is part of the log's topics, ``false`` if it is one of the log's data segments. - ``anonymous``: ``true`` if the event was declared as ``anonymous``. diff --git a/docs/analysing-compilation-output.rst b/docs/analysing-compilation-output.rst index 892b18b957..183f773223 100644 --- a/docs/analysing-compilation-output.rst +++ b/docs/analysing-compilation-output.rst @@ -11,7 +11,7 @@ visual diff of the assembly before and after a change is often very enlightening Consider the following contract (named, say ``contract.sol``): -.. code-block:: Solidity +.. code-block:: solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.5.0 <0.9.0; diff --git a/docs/assembly.rst b/docs/assembly.rst index b527ffbafd..a0a9d06dc7 100644 --- a/docs/assembly.rst +++ b/docs/assembly.rst @@ -9,7 +9,8 @@ Inline Assembly You can interleave Solidity statements with inline assembly in a language close to the one of the Ethereum Virtual Machine. This gives you more fine-grained control, -which is especially useful when you are enhancing the language by writing libraries. +which is especially useful when you are enhancing the language by writing libraries or +optimizing gas usage. The language used for inline assembly in Solidity is called :ref:`Yul ` and it is documented in its own section. This section will only cover @@ -162,7 +163,7 @@ their calldata offset (in bytes) and length (number of elements) using ``x.offse Both expressions can also be assigned to, but as for the static case, no validation will be performed to ensure that the resulting data area is within the bounds of ``calldatasize()``. -For local storage variables or state variables, a single Yul identifier +For local storage variables or state variables (including transient storage) a single Yul identifier is not sufficient, since they do not necessarily occupy a single full storage slot. Therefore, their "address" is composed of a slot and a byte-offset inside that slot. To retrieve the slot pointed to by the variable ``x``, you @@ -180,15 +181,18 @@ Local Solidity variables are available for assignments, for example: :force: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.7.0 <0.9.0; + pragma solidity >=0.8.28 <0.9.0; + // This will report a warning contract C { + bool transient a; uint b; - function f(uint x) public view returns (uint r) { + function f(uint x) public returns (uint r) { assembly { // We ignore the storage slot offset, we know it is zero // in this special case. r := mul(x, sload(b.slot)) + tstore(a.slot, true) } } } @@ -362,7 +366,7 @@ in memory is automatically considered memory-safe and does not need to be annota .. warning:: It is your responsibility to make sure that the assembly actually satisfies the memory model. If you annotate an assembly block as memory-safe, but violate one of the memory assumptions, this **will** lead to incorrect and - undefined behaviour that cannot easily be discovered by testing. + undefined behavior that cannot easily be discovered by testing. In case you are developing a library that is meant to be compatible across multiple versions of Solidity, you can use a special comment to annotate an assembly block as memory-safe: @@ -375,4 +379,25 @@ of Solidity, you can use a special comment to annotate an assembly block as memo } Note that we will disallow the annotation via comment in a future breaking release; so, if you are not concerned with -backwards-compatibility with older compiler versions, prefer using the dialect string. +backward-compatibility with older compiler versions, prefer using the dialect string. + +Advanced Safe Use of Memory +--------------------------- + +Beyond the strict definition of memory-safety given above, there are cases in which you may want to use more than 64 bytes +of scratch space starting at memory offset ``0``. If you are careful, it can be admissible to use memory up to (and not +including) offset ``0x80`` and still safely declare the assembly block as ``memory-safe``. +This is admissible under either of the following conditions: + +- By the end of the assembly block, the free memory pointer at offset ``0x40`` is restored to a sane value (i.e. it is either + restored to its original value or an increment of it due to a manual memory allocation), and the memory word at offset ``0x60`` + is restored to a value of zero. + +- The assembly block terminates, i.e. execution can never return to high-level Solidity code. This is the case, for example, + if your assembly block unconditionally ends in calling the ``revert`` opcode. + +Furthermore, you need to be aware that the default-value of dynamic arrays in Solidity point to memory offset ``0x60``, so +for the duration of temporarily changing the value at memory offset ``0x60``, you can no longer rely on getting accurate +length values when reading dynamic arrays, until you restore the zero value at ``0x60``. To be more precise, we only guarantee +safety when overwriting the zero pointer, if the remainder of the assembly snippet does not interact with the memory of +high-level Solidity objects (including by reading from offsets previously stored in variables). diff --git a/docs/brand-guide.rst b/docs/brand-guide.rst index 5601b16a8e..cf471c5e3c 100644 --- a/docs/brand-guide.rst +++ b/docs/brand-guide.rst @@ -67,7 +67,7 @@ When using the Solidity logo, please respect the Solidity logo guidelines. Solidity Logo Guidelines ======================== -.. image:: logo.svg +.. image:: solidity_logo.svg :width: 256 *(Right click on the logo to download it.)* diff --git a/docs/bugs.json b/docs/bugs.json index 9c9780c935..c853f95edd 100644 --- a/docs/bugs.json +++ b/docs/bugs.json @@ -1,4 +1,40 @@ [ + { + "uid": "SOL-2023-3", + "name": "VerbatimInvalidDeduplication", + "summary": "All ``verbatim`` blocks are considered identical by deduplicator and can incorrectly be unified when surrounded by identical opcodes.", + "description": "The block deduplicator is a step of the opcode-based optimizer which identifies equivalent assembly blocks and merges them into a single one. However, when blocks contained ``verbatim``, their comparison was performed incorrectly, leading to the collapse of assembly blocks which are identical except for the contents of the ``verbatim`` items. Since ``verbatim`` is only available in Yul, compilation of Solidity sources is not affected.", + "link": "https://blog.soliditylang.org/2023/11/08/verbatim-invalid-deduplication-bug/", + "introduced": "0.8.5", + "fixed": "0.8.23", + "severity": "low" + }, + { + "uid": "SOL-2023-2", + "name": "FullInlinerNonExpressionSplitArgumentEvaluationOrder", + "summary": "Optimizer sequences containing FullInliner do not preserve the evaluation order of arguments of inlined function calls in code that is not in expression-split form.", + "description": "Function call arguments in Yul are evaluated right to left. This order matters when the argument expressions have side-effects, and changing it may change contract behavior. FullInliner is an optimizer step that can replace a function call with the body of that function. The transformation involves assigning argument expressions to temporary variables, which imposes an explicit evaluation order. FullInliner was written with the assumption that this order does not necessarily have to match usual argument evaluation order because the argument expressions have no side-effects. In most circumstances this assumption is true because the default optimization step sequence contains the ExpressionSplitter step. ExpressionSplitter ensures that the code is in *expression-split form*, which means that function calls cannot appear nested inside expressions, and all function call arguments have to be variables. The assumption is, however, not guaranteed to be true in general. Version 0.6.7 introduced a setting allowing users to specify an arbitrary optimization step sequence, making it possible for the FullInliner to actually encounter argument expressions with side-effects, which can result in behavior differences between optimized and unoptimized bytecode. Contracts compiled without optimization or with the default optimization sequence are not affected. To trigger the bug the user has to explicitly choose compiler settings that contain a sequence with FullInliner step not preceded by ExpressionSplitter.", + "link": "https://blog.soliditylang.org/2023/07/19/full-inliner-non-expression-split-argument-evaluation-order-bug/", + "introduced": "0.6.7", + "fixed": "0.8.21", + "severity": "low", + "conditions": { + "yulOptimizer": true + } + }, + { + "uid": "SOL-2023-1", + "name": "MissingSideEffectsOnSelectorAccess", + "summary": "Accessing the ``.selector`` member on complex expressions leaves the expression unevaluated in the legacy code generation.", + "description": "When accessing the ``.selector`` member on an expression with side-effects, like an assignment, a function call or a conditional, the expression would not be evaluated in the legacy code generation. This would happen in expressions where the functions used in the expression were all known at compilation time, regardless of whether the whole expression could be evaluated at compilation time or not. Note that the code generated by the IR pipeline was unaffected and would behave as expected.", + "link": "https://blog.soliditylang.org/2023/07/19/missing-side-effects-on-selector-access-bug/", + "introduced": "0.6.2", + "fixed": "0.8.21", + "severity": "low", + "conditions": { + "viaIR": false + } + }, { "uid": "SOL-2022-7", "name": "StorageWriteRemovalBeforeConditionalTermination", diff --git a/docs/bugs.rst b/docs/bugs.rst index 75a23e4999..f7f18e9a7f 100644 --- a/docs/bugs.rst +++ b/docs/bugs.rst @@ -7,7 +7,7 @@ List of Known Bugs ################## Below, you can find a JSON-formatted list of some of the known security-relevant bugs in the -Solidity compiler. The file itself is hosted in the `Github repository +Solidity compiler. The file itself is hosted in the `GitHub repository `_. The list stretches back as far as version 0.3.0, bugs known to be present only in versions preceding that are not listed. @@ -68,7 +68,7 @@ conditions If no conditions are given, assume that the bug is present. check This field contains different checks that report whether the smart contract - contains the bug or not. The first type of check are Javascript regular + contains the bug or not. The first type of check are JavaScript regular expressions that are to be matched against the source code ("source-regex") if the bug is present. If there is no match, then the bug is very likely not present. If there is a match, the bug might be present. For improved diff --git a/docs/bugs_by_version.json b/docs/bugs_by_version.json index 4e7bca56b3..2c3b6487ae 100644 --- a/docs/bugs_by_version.json +++ b/docs/bugs_by_version.json @@ -1422,6 +1422,8 @@ }, "0.6.10": { "bugs": [ + "FullInlinerNonExpressionSplitArgumentEvaluationOrder", + "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "DataLocationChangeInInternalOverride", @@ -1436,6 +1438,8 @@ }, "0.6.11": { "bugs": [ + "FullInlinerNonExpressionSplitArgumentEvaluationOrder", + "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "DataLocationChangeInInternalOverride", @@ -1450,6 +1454,8 @@ }, "0.6.12": { "bugs": [ + "FullInlinerNonExpressionSplitArgumentEvaluationOrder", + "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "DataLocationChangeInInternalOverride", @@ -1464,6 +1470,7 @@ }, "0.6.2": { "bugs": [ + "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "NestedCalldataArrayAbiReencodingSizeValidation", @@ -1481,6 +1488,7 @@ }, "0.6.3": { "bugs": [ + "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "NestedCalldataArrayAbiReencodingSizeValidation", @@ -1498,6 +1506,7 @@ }, "0.6.4": { "bugs": [ + "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "NestedCalldataArrayAbiReencodingSizeValidation", @@ -1515,6 +1524,7 @@ }, "0.6.5": { "bugs": [ + "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "NestedCalldataArrayAbiReencodingSizeValidation", @@ -1532,6 +1542,7 @@ }, "0.6.6": { "bugs": [ + "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "NestedCalldataArrayAbiReencodingSizeValidation", @@ -1548,6 +1559,8 @@ }, "0.6.7": { "bugs": [ + "FullInlinerNonExpressionSplitArgumentEvaluationOrder", + "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "NestedCalldataArrayAbiReencodingSizeValidation", @@ -1564,6 +1577,8 @@ }, "0.6.8": { "bugs": [ + "FullInlinerNonExpressionSplitArgumentEvaluationOrder", + "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "NestedCalldataArrayAbiReencodingSizeValidation", @@ -1577,6 +1592,8 @@ }, "0.6.9": { "bugs": [ + "FullInlinerNonExpressionSplitArgumentEvaluationOrder", + "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "DataLocationChangeInInternalOverride", @@ -1592,6 +1609,8 @@ }, "0.7.0": { "bugs": [ + "FullInlinerNonExpressionSplitArgumentEvaluationOrder", + "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "DataLocationChangeInInternalOverride", @@ -1606,6 +1625,8 @@ }, "0.7.1": { "bugs": [ + "FullInlinerNonExpressionSplitArgumentEvaluationOrder", + "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "DataLocationChangeInInternalOverride", @@ -1621,6 +1642,8 @@ }, "0.7.2": { "bugs": [ + "FullInlinerNonExpressionSplitArgumentEvaluationOrder", + "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "DataLocationChangeInInternalOverride", @@ -1635,6 +1658,8 @@ }, "0.7.3": { "bugs": [ + "FullInlinerNonExpressionSplitArgumentEvaluationOrder", + "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "DataLocationChangeInInternalOverride", @@ -1648,6 +1673,8 @@ }, "0.7.4": { "bugs": [ + "FullInlinerNonExpressionSplitArgumentEvaluationOrder", + "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "DataLocationChangeInInternalOverride", @@ -1660,6 +1687,8 @@ }, "0.7.5": { "bugs": [ + "FullInlinerNonExpressionSplitArgumentEvaluationOrder", + "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "DataLocationChangeInInternalOverride", @@ -1672,6 +1701,8 @@ }, "0.7.6": { "bugs": [ + "FullInlinerNonExpressionSplitArgumentEvaluationOrder", + "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "DataLocationChangeInInternalOverride", @@ -1684,6 +1715,8 @@ }, "0.8.0": { "bugs": [ + "FullInlinerNonExpressionSplitArgumentEvaluationOrder", + "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "DataLocationChangeInInternalOverride", @@ -1696,6 +1729,8 @@ }, "0.8.1": { "bugs": [ + "FullInlinerNonExpressionSplitArgumentEvaluationOrder", + "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "DataLocationChangeInInternalOverride", @@ -1708,6 +1743,9 @@ }, "0.8.10": { "bugs": [ + "VerbatimInvalidDeduplication", + "FullInlinerNonExpressionSplitArgumentEvaluationOrder", + "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "DataLocationChangeInInternalOverride", @@ -1717,6 +1755,9 @@ }, "0.8.11": { "bugs": [ + "VerbatimInvalidDeduplication", + "FullInlinerNonExpressionSplitArgumentEvaluationOrder", + "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "DataLocationChangeInInternalOverride", @@ -1727,6 +1768,9 @@ }, "0.8.12": { "bugs": [ + "VerbatimInvalidDeduplication", + "FullInlinerNonExpressionSplitArgumentEvaluationOrder", + "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "DataLocationChangeInInternalOverride", @@ -1737,6 +1781,9 @@ }, "0.8.13": { "bugs": [ + "VerbatimInvalidDeduplication", + "FullInlinerNonExpressionSplitArgumentEvaluationOrder", + "MissingSideEffectsOnSelectorAccess", "StorageWriteRemovalBeforeConditionalTermination", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", @@ -1748,6 +1795,9 @@ }, "0.8.14": { "bugs": [ + "VerbatimInvalidDeduplication", + "FullInlinerNonExpressionSplitArgumentEvaluationOrder", + "MissingSideEffectsOnSelectorAccess", "StorageWriteRemovalBeforeConditionalTermination", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", @@ -1757,6 +1807,9 @@ }, "0.8.15": { "bugs": [ + "VerbatimInvalidDeduplication", + "FullInlinerNonExpressionSplitArgumentEvaluationOrder", + "MissingSideEffectsOnSelectorAccess", "StorageWriteRemovalBeforeConditionalTermination", "AbiReencodingHeadOverflowWithStaticArrayCleanup" ], @@ -1764,16 +1817,41 @@ }, "0.8.16": { "bugs": [ + "VerbatimInvalidDeduplication", + "FullInlinerNonExpressionSplitArgumentEvaluationOrder", + "MissingSideEffectsOnSelectorAccess", "StorageWriteRemovalBeforeConditionalTermination" ], "released": "2022-08-08" }, "0.8.17": { - "bugs": [], + "bugs": [ + "VerbatimInvalidDeduplication", + "FullInlinerNonExpressionSplitArgumentEvaluationOrder", + "MissingSideEffectsOnSelectorAccess" + ], "released": "2022-09-08" }, + "0.8.18": { + "bugs": [ + "VerbatimInvalidDeduplication", + "FullInlinerNonExpressionSplitArgumentEvaluationOrder", + "MissingSideEffectsOnSelectorAccess" + ], + "released": "2023-02-01" + }, + "0.8.19": { + "bugs": [ + "VerbatimInvalidDeduplication", + "FullInlinerNonExpressionSplitArgumentEvaluationOrder", + "MissingSideEffectsOnSelectorAccess" + ], + "released": "2023-02-22" + }, "0.8.2": { "bugs": [ + "FullInlinerNonExpressionSplitArgumentEvaluationOrder", + "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "DataLocationChangeInInternalOverride", @@ -1784,8 +1862,54 @@ ], "released": "2021-03-02" }, + "0.8.20": { + "bugs": [ + "VerbatimInvalidDeduplication", + "FullInlinerNonExpressionSplitArgumentEvaluationOrder", + "MissingSideEffectsOnSelectorAccess" + ], + "released": "2023-05-10" + }, + "0.8.21": { + "bugs": [ + "VerbatimInvalidDeduplication" + ], + "released": "2023-07-19" + }, + "0.8.22": { + "bugs": [ + "VerbatimInvalidDeduplication" + ], + "released": "2023-10-25" + }, + "0.8.23": { + "bugs": [], + "released": "2023-11-08" + }, + "0.8.24": { + "bugs": [], + "released": "2024-01-25" + }, + "0.8.25": { + "bugs": [], + "released": "2024-03-14" + }, + "0.8.26": { + "bugs": [], + "released": "2024-05-21" + }, + "0.8.27": { + "bugs": [], + "released": "2024-09-04" + }, + "0.8.28": { + "bugs": [], + "released": "2024-10-09" + }, "0.8.3": { "bugs": [ + "FullInlinerNonExpressionSplitArgumentEvaluationOrder", + "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "DataLocationChangeInInternalOverride", @@ -1797,6 +1921,8 @@ }, "0.8.4": { "bugs": [ + "FullInlinerNonExpressionSplitArgumentEvaluationOrder", + "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "DataLocationChangeInInternalOverride", @@ -1807,6 +1933,9 @@ }, "0.8.5": { "bugs": [ + "VerbatimInvalidDeduplication", + "FullInlinerNonExpressionSplitArgumentEvaluationOrder", + "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "DataLocationChangeInInternalOverride", @@ -1817,6 +1946,9 @@ }, "0.8.6": { "bugs": [ + "VerbatimInvalidDeduplication", + "FullInlinerNonExpressionSplitArgumentEvaluationOrder", + "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "DataLocationChangeInInternalOverride", @@ -1827,6 +1959,9 @@ }, "0.8.7": { "bugs": [ + "VerbatimInvalidDeduplication", + "FullInlinerNonExpressionSplitArgumentEvaluationOrder", + "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "DataLocationChangeInInternalOverride", @@ -1837,6 +1972,9 @@ }, "0.8.8": { "bugs": [ + "VerbatimInvalidDeduplication", + "FullInlinerNonExpressionSplitArgumentEvaluationOrder", + "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "DataLocationChangeInInternalOverride", @@ -1848,6 +1986,9 @@ }, "0.8.9": { "bugs": [ + "VerbatimInvalidDeduplication", + "FullInlinerNonExpressionSplitArgumentEvaluationOrder", + "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "DataLocationChangeInInternalOverride", diff --git a/docs/cheatsheet.rst b/docs/cheatsheet.rst index 412678953e..79aa6e65d5 100644 --- a/docs/cheatsheet.rst +++ b/docs/cheatsheet.rst @@ -2,16 +2,17 @@ Cheatsheet ********** -.. index:: operator; precedence +.. index:: operator;precedence Order of Precedence of Operators ================================ + .. include:: types/operator-precedence-table.rst -.. index:: assert, block, coinbase, difficulty, number, block;number, timestamp, block;timestamp, msg, data, gas, sender, value, gas price, origin, revert, require, keccak256, ripemd160, sha256, ecrecover, addmod, mulmod, cryptography, this, super, selfdestruct, balance, codehash, send +.. index:: abi;decode, abi;encode, abi;encodePacked, abi;encodeWithSelector, abi;encodeCall, abi;encodeWithSignature -Global Variables -================ +ABI Encoding and Decoding Functions +=================================== - ``abi.decode(bytes memory encodedData, (...)) returns (...)``: :ref:`ABI `-decodes the provided data. The types are given in parentheses as second argument. @@ -22,19 +23,58 @@ Global Variables - ``abi.encodeWithSelector(bytes4 selector, ...) returns (bytes memory)``: :ref:`ABI `-encodes the given arguments starting from the second and prepends the given four-byte selector - ``abi.encodeCall(function functionPointer, (...)) returns (bytes memory)``: ABI-encodes a call to ``functionPointer`` with the arguments found in the - tuple. Performs a full type-check, ensuring the types match the function signature. Result equals ``abi.encodeWithSelector(functionPointer.selector, (...))`` + tuple. Performs a full type-check, ensuring the types match the function signature. Result equals ``abi.encodeWithSelector(functionPointer.selector, ...)`` - ``abi.encodeWithSignature(string memory signature, ...) returns (bytes memory)``: Equivalent to ``abi.encodeWithSelector(bytes4(keccak256(bytes(signature))), ...)`` + +.. index:: bytes;concat, string;concat + +Members of ``bytes`` and ``string`` +==================================== + - ``bytes.concat(...) returns (bytes memory)``: :ref:`Concatenates variable number of arguments to one byte array` + - ``string.concat(...) returns (string memory)``: :ref:`Concatenates variable number of arguments to one string array` + +.. index:: address;balance, address;codehash, address;send, address;code, address;transfer + +Members of ``address`` +====================== + +- ``
.balance`` (``uint256``): balance of the :ref:`address` in Wei +- ``
.code`` (``bytes memory``): code at the :ref:`address` (can be empty) +- ``
.codehash`` (``bytes32``): the codehash of the :ref:`address` +- ``
.call(bytes memory) returns (bool, bytes memory)``: issue low-level ``CALL`` with the given payload, + returns success condition and return data +- ``
.delegatecall(bytes memory) returns (bool, bytes memory)``: issue low-level ``DELEGATECALL`` with the given payload, + returns success condition and return data +- ``
.staticcall(bytes memory) returns (bool, bytes memory)``: issue low-level ``STATICCALL`` with the given payload, + returns success condition and return data +- ``
.send(uint256 amount) returns (bool)``: send given amount of Wei to :ref:`address`, + returns ``false`` on failure +- ``
.transfer(uint256 amount)``: send given amount of Wei to :ref:`address`, throws on failure + +.. index:: blockhash, blobhash, block, block;basefee, block;blobbasefee, block;chainid, block;coinbase, block;difficulty, block;gaslimit, block;number, block;prevrandao, block;timestamp +.. index:: gasleft, msg;data, msg;sender, msg;sig, msg;value, tx;gasprice, tx;origin + +Block and Transaction Properties +================================ + +- ``blockhash(uint blockNumber) returns (bytes32)``: hash of the given block - only works for 256 most recent blocks +- ``blobhash(uint index) returns (bytes32)``: versioned hash of the ``index``-th blob associated with the current transaction. + A versioned hash consists of a single byte representing the version (currently ``0x01``), followed by the last 31 bytes + of the SHA256 hash of the KZG commitment (`EIP-4844 `_). + Returns zero if no blob with the given index exists. - ``block.basefee`` (``uint``): current block's base fee (`EIP-3198 `_ and `EIP-1559 `_) +- ``block.blobbasefee`` (``uint``): current block's blob base fee (`EIP-7516 `_ and `EIP-4844 `_) - ``block.chainid`` (``uint``): current chain id - ``block.coinbase`` (``address payable``): current block miner's address -- ``block.difficulty`` (``uint``): current block difficulty +- ``block.difficulty`` (``uint``): current block difficulty (``EVM < Paris``). For other EVM versions it behaves as a deprecated alias for ``block.prevrandao`` that will be removed in the next breaking release - ``block.gaslimit`` (``uint``): current block gaslimit - ``block.number`` (``uint``): current block number +- ``block.prevrandao`` (``uint``): random number provided by the beacon chain (``EVM >= Paris``) (see `EIP-4399 `_ ) - ``block.timestamp`` (``uint``): current block timestamp in seconds since Unix epoch - ``gasleft() returns (uint256)``: remaining gas - ``msg.data`` (``bytes``): complete calldata @@ -43,6 +83,12 @@ Global Variables - ``msg.value`` (``uint``): number of wei sent with the message - ``tx.gasprice`` (``uint``): gas price of the transaction - ``tx.origin`` (``address``): sender of the transaction (full call chain) + +.. index:: assert, require, revert + +Validations and Assertions +========================== + - ``assert(bool condition)``: abort execution and revert state changes if condition is ``false`` (use for internal error) - ``require(bool condition)``: abort execution and revert state changes if condition is ``false`` (use for malformed input or error in external component) @@ -50,7 +96,12 @@ Global Variables condition is ``false`` (use for malformed input or error in external component). Also provide error message. - ``revert()``: abort execution and revert state changes - ``revert(string memory message)``: abort execution and revert state changes providing an explanatory string -- ``blockhash(uint blockNumber) returns (bytes32)``: hash of the given block - only works for 256 most recent blocks + +.. index:: cryptography, keccak256, sha256, ripemd160, ecrecover, addmod, mulmod + +Mathematical and Cryptographic Functions +======================================== + - ``keccak256(bytes memory) returns (bytes32)``: compute the Keccak-256 hash of the input - ``sha256(bytes memory) returns (bytes32)``: compute the SHA-256 hash of the input - ``ripemd160(bytes memory) returns (bytes20)``: compute the RIPEMD-160 hash of the input @@ -60,15 +111,21 @@ Global Variables arbitrary precision and does not wrap around at ``2**256``. Assert that ``k != 0`` starting from version 0.5.0. - ``mulmod(uint x, uint y, uint k) returns (uint)``: compute ``(x * y) % k`` where the multiplication is performed with arbitrary precision and does not wrap around at ``2**256``. Assert that ``k != 0`` starting from version 0.5.0. + +.. index:: this, super, selfdestruct + +Contract-related +================ + - ``this`` (current contract's type): the current contract, explicitly convertible to ``address`` or ``address payable`` -- ``super``: the contract one level higher in the inheritance hierarchy -- ``selfdestruct(address payable recipient)``: destroy the current contract, sending its funds to the given address -- ``
.balance`` (``uint256``): balance of the :ref:`address` in Wei -- ``
.code`` (``bytes memory``): code at the :ref:`address` (can be empty) -- ``
.codehash`` (``bytes32``): the codehash of the :ref:`address` -- ``
.send(uint256 amount) returns (bool)``: send given amount of Wei to :ref:`address`, - returns ``false`` on failure -- ``
.transfer(uint256 amount)``: send given amount of Wei to :ref:`address`, throws on failure +- ``super``: a contract one level higher in the inheritance hierarchy +- ``selfdestruct(address payable recipient)``: send all funds to the given address and (only on EVMs before Cancun or when invoked within the transaction creating the contract) destroy the contract. + +.. index:: type;name, type;creationCode, type;runtimeCode, type;interfaceId, type;min, type;max + +Type Information +================ + - ``type(C).name`` (``string``): the name of the contract - ``type(C).creationCode`` (``bytes memory``): creation bytecode of the given contract, see :ref:`Type Information`. - ``type(C).runtimeCode`` (``bytes memory``): runtime bytecode of the given contract, see :ref:`Type Information`. @@ -103,12 +160,12 @@ Modifiers - ``pure`` for functions: Disallows modification or access of state. - ``view`` for functions: Disallows modification of state. - ``payable`` for functions: Allows them to receive Ether together with a call. -- ``constant`` for state variables: Disallows assignment (except initialisation), does not occupy storage slot. -- ``immutable`` for state variables: Allows exactly one assignment at construction time and is constant afterwards. Is stored in code. +- ``constant`` for state variables: Disallows assignment (except initialization), does not occupy storage slot. +- ``immutable`` for state variables: Allows assignment at construction time and is constant when deployed. Is stored in code. - ``anonymous`` for events: Does not store event signature as topic. - ``indexed`` for event parameters: Stores the parameter as topic. - ``virtual`` for functions and modifiers: Allows the function's or modifier's - behaviour to be changed in derived contracts. + behavior to be changed in derived contracts. - ``override``: States that this function, modifier or public state variable changes - the behaviour of a function or modifier in a base contract. + the behavior of a function or modifier in a base contract. diff --git a/docs/common-patterns.rst b/docs/common-patterns.rst index 1793ebd2f2..cbc219b53b 100644 --- a/docs/common-patterns.rst +++ b/docs/common-patterns.rst @@ -18,7 +18,7 @@ introduces a potential security risk. You may read more about this on the :ref:`security_considerations` page. The following is an example of the withdrawal pattern in practice in -a contract where the goal is to send the most money to the +a contract where the goal is to send the most of some compensation, e.g. Ether, to the contract in order to become the "richest", inspired by `King of the Ether `_. @@ -34,7 +34,7 @@ you receive the funds of the person who is now the richest. address public richest; uint public mostSent; - mapping (address => uint) pendingWithdrawals; + mapping(address => uint) pendingWithdrawals; /// The amount of Ether sent was not higher than /// the currently highest amount. @@ -55,7 +55,7 @@ you receive the funds of the person who is now the richest. function withdraw() public { uint amount = pendingWithdrawals[msg.sender]; // Remember to zero the pending refund before - // sending to prevent re-entrancy attacks + // sending to prevent reentrancy attacks pendingWithdrawals[msg.sender] = 0; payable(msg.sender).transfer(amount); } diff --git a/docs/conf.py b/docs/conf.py index 10aa406f28..f57a3b5b90 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -31,10 +31,60 @@ def setup(sphinx): sphinx.add_lexer('Solidity', SolidityLexer) sphinx.add_lexer('Yul', YulLexer) + sphinx.add_css_file('css/fonts.css') sphinx.add_css_file('css/custom.css') + sphinx.add_css_file('css/custom-dark.css') + sphinx.add_css_file('css/pygments.css') + +# -- RTD GitHub configuration --------------------------------------------- + +# Taken from: +# https://github.com/readthedocs/readthedocs.org/blob/e366e7fc8649fbcf1b6d06ecc12c5f7766144c46/readthedocs/projects/constants.py#L350 +GITHUB_REGEXS = [ + re.compile(r"github.com/(.+)/(.+)(?:\.git){1}$"), + # This must come before the one without a / to make sure we don't capture the / + re.compile(r"github.com/(.+)/(.+)/"), + re.compile(r"github.com/(.+)/(.+)"), + re.compile(r"github.com:(.+)/(.+)\.git$"), +] -# -- General configuration ------------------------------------------------ +# Taken and adapted from: +# https://github.com/readthedocs/readthedocs.org/blob/e366e7fc8649fbcf1b6d06ecc12c5f7766144c46/readthedocs/builds/utils.py#L24 +def get_github_username_repo(url): + if "github" in url: + for regex in GITHUB_REGEXS: + match = regex.search(url) + if match: + return match.groups() + return (None, None) + +display_github = False +# NOTE: RTD_DISPLAY_GITHUB, RTD_GITHUB_USER and RTD_GITHUB_REPO are set in the RTD project settings +github_user = os.getenv("RTD_GITHUB_USER") +github_repo = os.getenv("RTD_GITHUB_REPO") + +if github_user and github_repo: + display_github = True +else: + git_clone_url = os.getenv("READTHEDOCS_GIT_CLONE_URL") + if git_clone_url: + github_user, github_repo = get_github_username_repo(git_clone_url) + if github_user and github_repo: + display_github = True + +display_github_env = os.getenv("RTD_DISPLAY_GITHUB") +if display_github_env: + display_github = display_github_env.lower() == "true" + +html_context = { + "display_github": display_github, + "github_user": github_user, + "github_repo": github_repo, + "github_version": os.getenv("READTHEDOCS_VERSION", "develop"), + "conf_py_path": "/docs/", +} +# -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' @@ -45,6 +95,7 @@ def setup(sphinx): 'sphinx_a4doc', 'html_extra_template_renderer', 'remix_code_links', + 'sphinx.ext.imgconverter', ] a4_base_path = os.path.dirname(__file__) + '/grammar' @@ -63,7 +114,7 @@ def setup(sphinx): # General information about the project. project = 'Solidity' -project_copyright = '2016-2021, Ethereum' +project_copyright = '2016-2024, The Solidity Authors' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -131,7 +182,6 @@ def setup(sphinx): # documentation. html_theme_options = { 'logo_only': True, - 'style_nav_header_background': '#65afff', 'display_version': True, } @@ -147,12 +197,12 @@ def setup(sphinx): # The name of an image file (relative to this directory) to place at the top # of the sidebar. -html_logo = "logo.svg" +# html_logo = "logo.svg" # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. -#html_favicon = None +html_favicon = "_static/img/favicon.ico" # 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, @@ -161,7 +211,7 @@ def setup(sphinx): html_css_files = ["css/toggle.css"] -html_js_files = ["js/toggle.js"] +html_js_files = ["js/constants.js", "js/initialize.js", "js/toggle.js"] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied @@ -209,7 +259,7 @@ def setup(sphinx): #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True +html_show_sphinx = False # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True diff --git a/docs/contracts.rst b/docs/contracts.rst index aee26c95af..588b1da00d 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -20,6 +20,8 @@ There is no "cron" concept in Ethereum to call a function at a particular event .. include:: contracts/function-modifiers.rst +.. include:: contracts/transient-storage.rst + .. include:: contracts/constant-state-variables.rst .. include:: contracts/functions.rst diff --git a/docs/contracts/constant-state-variables.rst b/docs/contracts/constant-state-variables.rst index 2e94397b12..4e96987354 100644 --- a/docs/contracts/constant-state-variables.rst +++ b/docs/contracts/constant-state-variables.rst @@ -13,8 +13,9 @@ for ``immutable``, it can still be assigned at construction time. It is also possible to define ``constant`` variables at the file level. -The compiler does not reserve a storage slot for these variables, and every occurrence is -replaced by the respective value. +Every occurrence of such a variable in the source is replaced by its underlying value +and the compiler does not reserve a storage slot for it. +It cannot be assigned a slot in transient storage using the ``transient`` keyword either. Compared to regular state variables, the gas costs of constant and immutable variables are much lower. For a constant variable, the expression assigned to it is copied to @@ -30,19 +31,23 @@ Not all types for constants and immutables are implemented at this time. The onl .. code-block:: solidity // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.7.4; + pragma solidity ^0.8.21; uint constant X = 32**22 + 8; contract C { string constant TEXT = "abc"; bytes32 constant MY_HASH = keccak256("abc"); - uint immutable decimals; + uint immutable decimals = 18; uint immutable maxBalance; address immutable owner = msg.sender; constructor(uint decimals_, address ref) { - decimals = decimals_; + if (decimals_ != 0) + // Immutables are only immutable when deployed. + // At construction time they can be assigned to any number of times. + decimals = decimals_; + // Assignments to immutables can even access the environment. maxBalance = ref.balance; } @@ -74,25 +79,34 @@ Immutable ========= Variables declared as ``immutable`` are a bit less restricted than those -declared as ``constant``: Immutable variables can be assigned an arbitrary -value in the constructor of the contract or at the point of their declaration. -They can be assigned only once and can, from that point on, be read even during -construction time. +declared as ``constant``: Immutable variables can be assigned a +value at construction time. +The value can be changed at any time before deployment and then it becomes permanent. + +One additional restriction is that immutables can only be assigned to inside expressions for which +there is no possibility of being executed after creation. +This excludes all modifier definitions and functions other than constructors. + +There are no restrictions on reading immutable variables. +The read is even allowed to happen before the variable is written to for the first time because variables in +Solidity always have a well-defined initial value. +For this reason it is also allowed to never explicitly assign a value to an immutable. + +.. warning:: + When accessing immutables at construction time, please keep the :ref:`initialization order + ` in mind. + Even if you provide an explicit initializer, some expressions may end up being evaluated before + that initializer, especially when they are at a different level in inheritance hierarchy. + +.. note:: + Before Solidity 0.8.21 initialization of immutable variables was more restrictive. + Such variables had to be initialized exactly once at construction time and could not be read + before then. The contract creation code generated by the compiler will modify the contract's runtime code before it is returned by replacing all references to immutables with the values assigned to them. This is important if you are comparing the runtime code generated by the compiler with the one actually stored in the -blockchain. - -.. note:: - Immutables that are assigned at their declaration are only considered - initialized once the constructor of the contract is executing. - This means you cannot initialize immutables inline with a value - that depends on another immutable. You can do this, however, - inside the constructor of the contract. - - This is a safeguard against different interpretations about the order - of state variable initialization and constructor execution, especially - with regards to inheritance. +blockchain. The compiler outputs where these immutables are located in the deployed bytecode +in the ``immutableReferences`` field of the :ref:`compiler JSON standard output `. diff --git a/docs/contracts/creating-contracts.rst b/docs/contracts/creating-contracts.rst index f574a5a6aa..8eb4e5f56c 100644 --- a/docs/contracts/creating-contracts.rst +++ b/docs/contracts/creating-contracts.rst @@ -8,7 +8,7 @@ Contracts can be created "from outside" via Ethereum transactions or from within IDEs, such as `Remix `_, make the creation process seamless using UI elements. -One way to create contracts programmatically on Ethereum is via the JavaScript API `web3.js `_. +One way to create contracts programmatically on Ethereum is via the JavaScript API `web3.js `_. It has a function called `web3.eth.Contract `_ to facilitate contract creation. diff --git a/docs/contracts/errors.rst b/docs/contracts/errors.rst index 19577a3872..d345b4eacf 100644 --- a/docs/contracts/errors.rst +++ b/docs/contracts/errors.rst @@ -1,22 +1,25 @@ -.. index:: ! error, revert, ! selector; of an error +.. index:: ! error, revert, require, ! selector; of an error .. _errors: -******************************* -Errors and the Revert Statement -******************************* +************* +Custom Errors +************* Errors in Solidity provide a convenient and gas-efficient way to explain to the user why an operation failed. They can be defined inside and outside of contracts (including interfaces and libraries). They have to be used together with the :ref:`revert statement ` -which causes -all changes in the current call to be reverted and passes the error data back to the -caller. +or the :ref:`require function `. +In the case of ``revert`` statements, or ``require`` calls where the condition is evaluated to be false, +all changes in the current call are reverted, and the error data passed back to the caller. + +The example below shows custom error usage with the ``revert`` statement in function ``transferWithRevertError``, +as well as the newer approach with ``require`` in function ``transferWithRequireError``. .. code-block:: solidity // SPDX-License-Identifier: GPL-3.0 - pragma solidity ^0.8.4; + pragma solidity ^0.8.27; /// Insufficient balance for transfer. Needed `required` but only /// `available` available. @@ -26,7 +29,7 @@ caller. contract TestToken { mapping(address => uint) balance; - function transfer(address to, uint256 amount) public { + function transferWithRevertError(address to, uint256 amount) public { if (amount > balance[msg.sender]) revert InsufficientBalance({ available: balance[msg.sender], @@ -35,12 +38,22 @@ caller. balance[msg.sender] -= amount; balance[to] += amount; } + function transferWithRequireError(address to, uint256 amount) public { + require(amount <= balance[msg.sender], InsufficientBalance(balance[msg.sender], amount)); + balance[msg.sender] -= amount; + balance[to] += amount; + } // ... } +Another important detail to mention when it comes to using ``require`` with custom errors, is that memory +allocation for the error-based revert reason will only happen in the reverting case, which, along with +optimization of constants and string literals makes this about as gas-efficient as the +``if (!condition) revert CustomError(args)`` pattern. + Errors cannot be overloaded or overridden but are inherited. The same error can be defined in multiple places as long as the scopes are distinct. -Instances of errors can only be created using ``revert`` statements. +Instances of errors can only be created using ``revert`` statements, or as the second argument to ``require`` functions. The error creates data that is then passed to the caller with the revert operation to either return to the off-chain component or catch it in a :ref:`try/catch statement `. @@ -65,8 +78,7 @@ The selector consists of the first four bytes of the keccak256-hash of the signa only the name of the error is relevant, not the contract or file where it is defined. The statement ``require(condition, "description");`` would be equivalent to -``if (!condition) revert Error("description")`` if you could define -``error Error(string)``. +``if (!condition) revert Error("description")`` if you could define ``error Error(string)``. Note, however, that ``Error`` is a built-in type and cannot be defined in user-supplied code. Similarly, a failing ``assert`` or similar conditions will revert with an error diff --git a/docs/contracts/events.rst b/docs/contracts/events.rst index 8618e7524b..8c73c31603 100644 --- a/docs/contracts/events.rst +++ b/docs/contracts/events.rst @@ -9,12 +9,13 @@ Events Solidity events give an abstraction on top of the EVM's logging functionality. Applications can subscribe and listen to these events through the RPC interface of an Ethereum client. -Events are inheritable members of contracts. When you call them, they cause the +Events can be defined at file level or as inheritable members of contracts (including interfaces and libraries). +When you call them, they cause the arguments to be stored in the transaction's log - a special data structure -in the blockchain. These logs are associated with the address of the contract, +in the blockchain. These logs are associated with the address of the contract that emitted them, are incorporated into the blockchain, and stay there as long as a block is accessible (forever as of now, but this might -change with Serenity). The Log and its event data is not accessible from within +change in the future). The Log and its event data is not accessible from within contracts (not even from the contract that created them). It is possible to request a Merkle proof for logs, so if @@ -151,6 +152,6 @@ The output of the above looks like the following (trimmed): Additional Resources for Understanding Events ============================================= -- `Javascript documentation `_ +- `JavaScript documentation `_ - `Example usage of events `_ - `How to access them in js `_ diff --git a/docs/contracts/function-modifiers.rst b/docs/contracts/function-modifiers.rst index f231195448..2fd0357db0 100644 --- a/docs/contracts/function-modifiers.rst +++ b/docs/contracts/function-modifiers.rst @@ -6,7 +6,7 @@ Function Modifiers ****************** -Modifiers can be used to change the behaviour of functions in a declarative way. +Modifiers can be used to change the behavior of functions in a declarative way. For example, you can use a modifier to automatically check a condition prior to executing the function. @@ -40,16 +40,6 @@ if they are marked ``virtual``. For details, please see } } - contract destructible is owned { - // This contract inherits the `onlyOwner` modifier from - // `owned` and applies it to the `destroy` function, which - // causes that calls to `destroy` only have an effect if - // they are made by the stored owner. - function destroy() public onlyOwner { - selfdestruct(owner); - } - } - contract priced { // Modifiers can receive arguments: modifier costs(uint price) { @@ -59,8 +49,8 @@ if they are marked ``virtual``. For details, please see } } - contract Register is priced, destructible { - mapping (address => bool) registeredAddresses; + contract Register is priced, owned { + mapping(address => bool) registeredAddresses; uint price; constructor(uint initialPrice) { price = initialPrice; } @@ -72,6 +62,9 @@ if they are marked ``virtual``. For details, please see registeredAddresses[msg.sender] = true; } + // This contract inherits the `onlyOwner` modifier from + // the `owned` contract. As a result, calls to `changePrice` will + // only take effect if they are made by the stored owner. function changePrice(uint price_) public onlyOwner { price = price_; } @@ -131,7 +124,7 @@ variables are set to their :ref:`default values` just as if the f body. The ``_`` symbol can appear in the modifier multiple times. Each occurrence is replaced with -the function body. +the function body, and the function returns the return value of the final occurrence. Arbitrary expressions are allowed for modifier arguments and in this context, all symbols visible from the function are visible in the modifier. Symbols diff --git a/docs/contracts/functions.rst b/docs/contracts/functions.rst index 902ad1ce5a..a3a2755388 100644 --- a/docs/contracts/functions.rst +++ b/docs/contracts/functions.rst @@ -1,4 +1,4 @@ -.. index:: ! functions +.. index:: ! functions, ! function;free .. _functions: @@ -174,7 +174,7 @@ Functions can be declared ``view`` in which case they promise not to modify the The following statements are considered modifying the state: -#. Writing to state variables. +#. Writing to state variables (storage and transient storage). #. :ref:`Emitting events `. #. :ref:`Creating other contracts `. #. Using ``selfdestruct``. @@ -226,7 +226,7 @@ This means that reading from ``immutable`` variables can be a non-pure operation In addition to the list of state modifying statements explained above, the following are considered reading from the state: -#. Reading from state variables. +#. Reading from state variables (storage and transient storage). #. Accessing ``address(this).balance`` or ``
.balance``. #. Accessing any of the members of ``block``, ``tx``, ``msg`` (with the exception of ``msg.sig`` and ``msg.data``). #. Calling any function not marked ``pure``. @@ -250,7 +250,7 @@ Reverting a state change is not considered a "state modification", as only chang state made previously in code that did not have the ``view`` or ``pure`` restriction are reverted and that code has the option to catch the ``revert`` and not pass it on. -This behaviour is also in line with the ``STATICCALL`` opcode. +This behavior is also in line with the ``STATICCALL`` opcode. .. warning:: It is not possible to prevent functions from reading the state at the level @@ -267,7 +267,7 @@ This behaviour is also in line with the ``STATICCALL`` opcode. .. note:: Prior to version 0.4.17 the compiler did not enforce that ``pure`` is not reading the state. - It is a compile-time type check, which can be circumvented doing invalid explicit conversions + It is a compile-time type check, which can be circumvented by doing invalid explicit conversions between contract types, because the compiler can verify that the type of the contract does not do state-changing operations, but it cannot check that the contract that will be called at runtime is actually of that type. @@ -277,7 +277,7 @@ This behaviour is also in line with the ``STATICCALL`` opcode. Special Functions ================= -.. index:: ! receive ether function, function;receive ! receive +.. index:: ! receive ether function, function;receive, ! receive .. _receive-ether-function: diff --git a/docs/contracts/inheritance.rst b/docs/contracts/inheritance.rst index a33a36d27b..fccfee6095 100644 --- a/docs/contracts/inheritance.rst +++ b/docs/contracts/inheritance.rst @@ -41,26 +41,26 @@ Details are given in the following example. // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.7.0 <0.9.0; - contract Owned { - constructor() { owner = payable(msg.sender); } address payable owner; + constructor() { owner = payable(msg.sender); } } - // Use `is` to derive from another contract. Derived // contracts can access all non-private members including // internal functions and state variables. These cannot be // accessed externally via `this`, though. - contract Destructible is Owned { + contract Emittable is Owned { + event Emitted(); + // The keyword `virtual` means that the function can change - // its behaviour in derived classes ("overriding"). - function destroy() virtual public { - if (msg.sender == owner) selfdestruct(owner); + // its behavior in derived classes ("overriding"). + function emitEvent() virtual public { + if (msg.sender == owner) + emit Emitted(); } } - // These abstract contracts are only provided to make the // interface known to the compiler. Note the function // without body. If a contract does not implement all @@ -69,37 +69,35 @@ Details are given in the following example. function lookup(uint id) public virtual returns (address adr); } - abstract contract NameReg { function register(bytes32 name) public virtual; function unregister() public virtual; } - // Multiple inheritance is possible. Note that `Owned` is - // also a base class of `Destructible`, yet there is only a single + // also a base class of `Emittable`, yet there is only a single // instance of `Owned` (as for virtual inheritance in C++). - contract Named is Owned, Destructible { + contract Named is Owned, Emittable { constructor(bytes32 name) { Config config = Config(0xD5f9D8D94886E70b06E474c3fB14Fd43E2f23970); NameReg(config.lookup(1)).register(name); } // Functions can be overridden by another function with the same name and - // the same number/types of inputs. If the overriding function has different + // the same number/types of inputs. If the overriding function has different // types of output parameters, that causes an error. // Both local and message-based function calls take these overrides // into account. // If you want the function to override, you need to use the // `override` keyword. You need to specify the `virtual` keyword again // if you want this function to be overridden again. - function destroy() public virtual override { + function emitEvent() public virtual override { if (msg.sender == owner) { Config config = Config(0xD5f9D8D94886E70b06E474c3fB14Fd43E2f23970); NameReg(config.lookup(1)).unregister(); // It is still possible to call a specific // overridden function. - Destructible.destroy(); + Emittable.emitEvent(); } } } @@ -108,22 +106,22 @@ Details are given in the following example. // If a constructor takes an argument, it needs to be // provided in the header or modifier-invocation-style at // the constructor of the derived contract (see below). - contract PriceFeed is Owned, Destructible, Named("GoldFeed") { + contract PriceFeed is Owned, Emittable, Named("GoldFeed") { + uint info; + function updateInfo(uint newInfo) public { if (msg.sender == owner) info = newInfo; } // Here, we only specify `override` and not `virtual`. // This means that contracts deriving from `PriceFeed` - // cannot change the behaviour of `destroy` anymore. - function destroy() public override(Destructible, Named) { Named.destroy(); } + // cannot change the behavior of `emitEvent` anymore. + function emitEvent() public override(Emittable, Named) { Named.emitEvent(); } function get() public view returns(uint r) { return info; } - - uint info; } -Note that above, we call ``Destructible.destroy()`` to "forward" the -destruction request. The way this is done is problematic, as +Note that above, we call ``Emittable.emitEvent()`` to "forward" the +emit event request. The way this is done is problematic, as seen in the following example: .. code-block:: solidity @@ -131,68 +129,109 @@ seen in the following example: // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.7.0 <0.9.0; - contract owned { - constructor() { owner = payable(msg.sender); } + contract Owned { address payable owner; + constructor() { owner = payable(msg.sender); } } - contract Destructible is owned { - function destroy() public virtual { - if (msg.sender == owner) selfdestruct(owner); + contract Emittable is Owned { + event Emitted(); + + function emitEvent() virtual public { + if (msg.sender == owner) { + emit Emitted(); + } } } - contract Base1 is Destructible { - function destroy() public virtual override { /* do cleanup 1 */ Destructible.destroy(); } + contract Base1 is Emittable { + event Base1Emitted(); + function emitEvent() public virtual override { + /* Here, we emit an event to simulate some Base1 logic */ + emit Base1Emitted(); + Emittable.emitEvent(); + } } - contract Base2 is Destructible { - function destroy() public virtual override { /* do cleanup 2 */ Destructible.destroy(); } + contract Base2 is Emittable { + event Base2Emitted(); + function emitEvent() public virtual override { + /* Here, we emit an event to simulate some Base2 logic */ + emit Base2Emitted(); + Emittable.emitEvent(); + } } contract Final is Base1, Base2 { - function destroy() public override(Base1, Base2) { Base2.destroy(); } + event FinalEmitted(); + function emitEvent() public override(Base1, Base2) { + /* Here, we emit an event to simulate some Final logic */ + emit FinalEmitted(); + Base2.emitEvent(); + } } -A call to ``Final.destroy()`` will call ``Base2.destroy`` because we specify it +A call to ``Final.emitEvent()`` will call ``Base2.emitEvent`` because we specify it explicitly in the final override, but this function will bypass -``Base1.destroy``. The way around this is to use ``super``: +``Base1.emitEvent``, resulting in the following sequence of events: +``FinalEmitted -> Base2Emitted -> Emitted``, instead of the expected sequence: +``FinalEmitted -> Base2Emitted -> Base1Emitted -> Emitted``. +The way around this is to use ``super``: .. code-block:: solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.7.0 <0.9.0; - contract owned { - constructor() { owner = payable(msg.sender); } + contract Owned { address payable owner; + constructor() { owner = payable(msg.sender); } } - contract Destructible is owned { - function destroy() virtual public { - if (msg.sender == owner) selfdestruct(owner); + contract Emittable is Owned { + event Emitted(); + + function emitEvent() virtual public { + if (msg.sender == owner) { + emit Emitted(); + } } } - contract Base1 is Destructible { - function destroy() public virtual override { /* do cleanup 1 */ super.destroy(); } + contract Base1 is Emittable { + event Base1Emitted(); + function emitEvent() public virtual override { + /* Here, we emit an event to simulate some Base1 logic */ + emit Base1Emitted(); + super.emitEvent(); + } } - contract Base2 is Destructible { - function destroy() public virtual override { /* do cleanup 2 */ super.destroy(); } + contract Base2 is Emittable { + event Base2Emitted(); + function emitEvent() public virtual override { + /* Here, we emit an event to simulate some Base2 logic */ + emit Base2Emitted(); + super.emitEvent(); + } } contract Final is Base1, Base2 { - function destroy() public override(Base1, Base2) { super.destroy(); } + event FinalEmitted(); + function emitEvent() public override(Base1, Base2) { + /* Here, we emit an event to simulate some Final logic */ + emit FinalEmitted(); + super.emitEvent(); + } } -If ``Base2`` calls a function of ``super``, it does not simply +If ``Final`` calls a function of ``super``, it does not simply call this function on one of its base contracts. Rather, it calls this function on the next base contract in the final -inheritance graph, so it will call ``Base1.destroy()`` (note that +inheritance graph, so it will call ``Base1.emitEvent()`` (note that the final inheritance sequence is -- starting with the most -derived contract: Final, Base2, Base1, Destructible, owned). +derived contract: Final, Base2, Base1, Emittable, Owned). The actual function that is called when using super is not known in the context of the class where it is used, although its type is known. This is similar for ordinary @@ -291,7 +330,7 @@ and ends at a contract mentioning a function with that signature that does not override. If you do not mark a function that overrides as ``virtual``, derived -contracts can no longer change the behaviour of that function. +contracts can no longer change the behavior of that function. .. note:: @@ -396,7 +435,7 @@ Constructors A constructor is an optional function declared with the ``constructor`` keyword which is executed upon contract creation, and where you can run contract -initialisation code. +initialization code. Before the constructor code is executed, state variables are initialised to their specified value if you initialise them inline, or their :ref:`default value` if you do not. @@ -434,11 +473,11 @@ You can use internal parameters in a constructor (for example storage pointers). the contract has to be marked :ref:`abstract `, because these parameters cannot be assigned valid values from outside but only through the constructors of derived contracts. -.. warning :: +.. warning:: Prior to version 0.4.22, constructors were defined as functions with the same name as the contract. This syntax was deprecated and is not allowed anymore in version 0.5.0. -.. warning :: +.. warning:: Prior to version 0.7.0, you had to specify the visibility of constructors as either ``internal`` or ``public``. @@ -485,7 +524,7 @@ One way is directly in the inheritance list (``is Base(7)``). The other is in the way a modifier is invoked as part of the derived constructor (``Base(y * y)``). The first way to do it is more convenient if the constructor argument is a -constant and defines the behaviour of the contract or +constant and defines the behavior of the contract or describes it. The second way has to be used if the constructor arguments of the base depend on those of the derived contract. Arguments have to be given either in the @@ -588,9 +627,11 @@ One area where inheritance linearization is especially important and perhaps not Inheriting Different Kinds of Members of the Same Name ====================================================== -It is an error when any of the following pairs in a contract have the same name due to inheritance: - - a function and a modifier - - a function and an event - - an event and a modifier +The only situations where, due to inheritance, a contract may contain multiple definitions sharing +the same name are: -As an exception, a state variable getter can override an external function. +- Overloading of functions. +- Overriding of virtual functions. +- Overriding of external virtual functions by state variable getters. +- Overriding of virtual modifiers. +- Overloading of events. diff --git a/docs/contracts/interfaces.rst b/docs/contracts/interfaces.rst index cc71cf64e6..a3085667b7 100644 --- a/docs/contracts/interfaces.rst +++ b/docs/contracts/interfaces.rst @@ -65,7 +65,7 @@ inheritance. Types defined inside interfaces and other contract-like structures can be accessed from other contracts: ``Token.TokenType`` or ``Token.Coin``. -.. warning: +.. warning:: Interfaces have supported ``enum`` types since :doc:`Solidity version 0.5.0 <050-breaking-changes>`, make sure the pragma version specifies this version as a minimum. diff --git a/docs/contracts/libraries.rst b/docs/contracts/libraries.rst index 7256ebe031..a3c04ba064 100644 --- a/docs/contracts/libraries.rst +++ b/docs/contracts/libraries.rst @@ -196,7 +196,7 @@ It is possible to obtain the address of a library by converting the library type to the ``address`` type, i.e. using ``address(LibraryName)``. As the compiler does not know the address where the library will be deployed, the compiled hex code -will contain placeholders of the form ``__$30bbc0abd4d6364515865950d3e0d10953$__``. The placeholder +will contain placeholders of the form ``__$30bbc0abd4d6364515865950d3e0d10953$__`` `(format was different `_. The placeholder is a 34 character prefix of the hex encoding of the keccak256 hash of the fully qualified library name, which would be for example ``libraries/bigint.sol:BigInt`` if the library was stored in a file called ``bigint.sol`` in a ``libraries/`` directory. Such bytecode is incomplete and should not be diff --git a/docs/contracts/transient-storage.rst b/docs/contracts/transient-storage.rst new file mode 100644 index 0000000000..b2d0be2922 --- /dev/null +++ b/docs/contracts/transient-storage.rst @@ -0,0 +1,172 @@ +.. index:: ! transient storage, ! transient, tstore, tload + +.. _transient-storage: + +***************** +Transient Storage +***************** + +Transient storage is another data location besides memory, storage, calldata +(and return-data and code) which was introduced alongside its respective opcodes +``TSTORE`` and ``TLOAD`` by `EIP-1153 `_. +This new data location behaves as a key-value store similar to storage with the main +difference being that data in transient storage is not permanent, but is scoped to +the current transaction only, after which it will be reset to zero. +Since the content of transient storage has very limited lifetime and size, +it does not need to be stored permanently as a part of state +and the associated gas costs are much lower than in case of storage. +EVM version ``cancun`` or newer is required for transient storage to be available. + +Transient storage variables cannot be initialized in place, i.e., they cannot be assigned +to upon declaration, since the value would be cleared at the end of the creation transaction, +rendering the initialization ineffective. +Transient variables will be :ref:`default value` initialized depending on +their underlying type. +``constant`` and ``immutable`` variables conflict with transient storage, since +their values are either inlined or directly stored in code. + +Transient storage variables have completely independent address space from storage, +so that the order of transient state variables does not affect the layout of storage +state variables and vice-versa. +They do need distinct names though because all state variables share the same namespace. +It is also important to note that the values in transient storage are packed in the +same fashion as those in persistent storage. +See :ref:`Storage Layout ` for more information. + +Besides that, transient variables can have visibility as well and ``public`` ones will +have a getter function generated automatically as usual. + +Note that, currently, such use of ``transient`` as a data location is only allowed for +:ref:`value type ` state variable declarations. +Reference types, such as arrays, mappings and structs, as well as local or parameter +variables are not yet supported. + +An expected canonical use case for transient storage is cheaper reentrancy locks, +which can be readily implemented with the opcodes as showcased next. + +.. code-block:: solidity + + // SPDX-License-Identifier: GPL-3.0 + pragma solidity ^0.8.28; + + contract Generosity { + mapping(address => bool) sentGifts; + bool transient locked; + + modifier nonReentrant { + require(!locked, "Reentrancy attempt"); + locked = true; + _; + // Unlocks the guard, making the pattern composable. + // After the function exits, it can be called again, even in the same transaction. + locked = false; + } + + function claimGift() nonReentrant public { + require(address(this).balance >= 1 ether); + require(!sentGifts[msg.sender]); + (bool success, ) = msg.sender.call{value: 1 ether}(""); + require(success); + + // In a reentrant function, doing this last would open up the vulnerability + sentGifts[msg.sender] = true; + } + } + +Transient storage is private to the contract that owns it, in the same way as persistent storage. +Only owning contract frames may access their transient storage, and when they do, all the frames access the same transient store. + +Transient storage is part of the EVM state and is subject to the same mutability enforcements +as persistent storage. As such, any read access to it is not ``pure`` and writing access is not ``view``. + +If the ``TSTORE`` opcode is called within the context of a ``STATICCALL``, +it will result in an exception instead of performing the modification. +``TLOAD`` is allowed within the context of a ``STATICCALL``. + +When transient storage is used in the context of ``DELEGATECALL`` or ``CALLCODE``, +then the owning contract of the transient storage is the contract that issued ``DELEGATECALL`` +or ``CALLCODE`` instruction (the caller) as with persistent storage. +When transient storage is used in the context of ``CALL`` or ``STATICCALL``, +then the owning contract of the transient storage is the contract that is the target +of the ``CALL`` or ``STATICCALL`` instruction (the callee). + +.. note:: + In the case of ``DELEGATECALL``, since references to transient storage variables + are currently not supported, it is not possible to pass those into library calls. + In libraries, access to transient storage is only possible using inline assembly. + +If a frame reverts, all writes to transient storage that took place between entry +to the frame and the return are reverted, including those that took place in inner calls. +The caller of an external call may employ a ``try ... catch`` block to prevent reverts +bubbling up from the inner calls. + +********************************************************************* +Composability of Smart Contracts and the Caveats of Transient Storage +********************************************************************* + +Given the caveats mentioned in the specification of EIP-1153, +in order to preserve the composability of your smart contract, +utmost care is recommended for more advanced use cases of transient storage. + +For smart contracts, composability is a very important design principle to achieve self-contained behaviour, +such that multiple calls into individual smart contracts can be composed to more complex applications. +So far the EVM largely guaranteed composable behaviour, since multiple calls into a smart contract +within a complex transaction are virtually indistinguishable from multiple calls to the contract +stretched over several transactions. However, transient storage allows a violation of this principle, +and incorrect use may lead to complex bugs that only surface when used across several calls. + +Let's illustrate the problem with a simple example: + +.. code-block:: solidity + + // SPDX-License-Identifier: GPL-3.0 + pragma solidity ^0.8.28; + + contract MulService { + uint transient multiplier; + function setMultiplier(uint mul) external { + multiplier = mul; + } + + function multiply(uint value) external view returns (uint) { + return value * multiplier; + } + } + +and a sequence of external calls: + +.. code-block:: solidity + + setMultiplier(42); + multiply(1); + multiply(2); + +If the example used memory or storage to store the multiplier, it would be fully composable. +It would not matter whether you split the sequence into separate transactions or grouped them in some way. +You would always get the same result: after ``multiplier`` is set to ``42``, the subsequent calls +would return ``42`` and ``84`` respectively. +This enables use cases such as batching calls from multiple transactions +together to reduce gas costs. +Transient storage potentially breaks such use cases since composability can no longer be taken for granted. +In the example, if the calls are not executed in the same transaction, then ``multiplier`` +is reset and the next calls to function ``multiply`` would always return ``0``. + +As another example, since transient storage is constructed as a relatively cheap key-value store, +a smart contract author may be tempted to use transient storage as a replacement for in-memory mappings +without keeping track of the modified keys in the mapping and thereby without clearing the mapping +at the end of the call. +This, however, can easily lead to unexpected behaviour in complex transactions, +in which values set by a previous call into the contract within the same transaction remain. + +The use of transient storage for reentrancy locks that are cleared at the end of the call frame +into the contract, is safe. +However, be sure to resist the temptation to save the 100 gas used for resetting the +reentrancy lock, since failing to do so, will restrict your contract to only one call +within a transaction, preventing its use in complex composed transactions, +which have been a cornerstone for complex applications on chain. + +It is recommend to generally always clear transient storage completely at the end of a call +into your smart contract to avoid these kinds of issues and to simplify +the analysis of the behaviour of your contract within complex transactions. +Check the `Security Considerations section of EIP-1153 `_ +for further details. \ No newline at end of file diff --git a/docs/contracts/using-for.rst b/docs/contracts/using-for.rst index 20299e1a4b..a05cb277c6 100644 --- a/docs/contracts/using-for.rst +++ b/docs/contracts/using-for.rst @@ -1,4 +1,4 @@ -.. index:: ! using for, library +.. index:: ! using for, library, ! operator;user-defined, function;free .. _using-for: @@ -6,37 +6,87 @@ Using For ********* -The directive ``using A for B;`` can be used to attach -functions (``A``) as member functions to any type (``B``). -These functions will receive the object they are called on +The directive ``using A for B`` can be used to attach +functions (``A``) as operators to user-defined value types +or as member functions to any type (``B``). +The member functions receive the object they are called on as their first parameter (like the ``self`` variable in Python). +The operator functions receive operands as parameters. It is valid either at file level or inside a contract, at contract level. The first part, ``A``, can be one of: -- A list of file-level or library functions (e.g. ``using {f, g, h, L.t} for uint;``) - - only those functions will be attached to the type as member functions. - Note that private library functions can only be specified when ``using for`` is inside the library. -- The name of a library (e.g. ``using L for uint;``) - - all non-private functions of the library are attached to the type. +- A list of functions, optionally with an operator name assigned (e.g. + ``using {f, g as +, h, L.t} for uint``). + If no operator is specified, the function can be either a library function or a free function and + is attached to the type as a member function. + Otherwise it must be a free function and it becomes the definition of that operator on the type. +- The name of a library (e.g. ``using L for uint``) - + all non-private functions of the library are attached to the type + as member functions At file level, the second part, ``B``, has to be an explicit type (without data location specifier). Inside contracts, you can also use ``*`` in place of the type (e.g. ``using L for *;``), which has the effect that all functions of the library ``L`` are attached to *all* types. -If you specify a library, *all* functions in the library get attached, +If you specify a library, *all* non-private functions in the library get attached, even those where the type of the first parameter does not match the type of the object. The type is checked at the point the function is called and function overload resolution is performed. -If you use a list of functions (e.g. ``using {f, g, h, L.t} for uint;``), +If you use a list of functions (e.g. ``using {f, g, h, L.t} for uint``), then the type (``uint``) has to be implicitly convertible to the first parameter of each of these functions. This check is performed even if none of these functions are called. +Note that private library functions can only be specified when ``using for`` is inside a library. + +If you define an operator (e.g. ``using {f as +} for T``), then the type (``T``) must be a +:ref:`user-defined value type ` and the definition must be a ``pure`` function. +Operator definitions must be global. +The following operators can be defined this way: + ++------------+----------+---------------------------------------------+ +| Category | Operator | Possible signatures | ++============+==========+=============================================+ +| Bitwise | ``&`` | ``function (T, T) pure returns (T)`` | +| +----------+---------------------------------------------+ +| | ``|`` | ``function (T, T) pure returns (T)`` | +| +----------+---------------------------------------------+ +| | ``^`` | ``function (T, T) pure returns (T)`` | +| +----------+---------------------------------------------+ +| | ``~`` | ``function (T) pure returns (T)`` | ++------------+----------+---------------------------------------------+ +| Arithmetic | ``+`` | ``function (T, T) pure returns (T)`` | +| +----------+---------------------------------------------+ +| | ``-`` | ``function (T, T) pure returns (T)`` | +| + +---------------------------------------------+ +| | | ``function (T) pure returns (T)`` | +| +----------+---------------------------------------------+ +| | ``*`` | ``function (T, T) pure returns (T)`` | +| +----------+---------------------------------------------+ +| | ``/`` | ``function (T, T) pure returns (T)`` | +| +----------+---------------------------------------------+ +| | ``%`` | ``function (T, T) pure returns (T)`` | ++------------+----------+---------------------------------------------+ +| Comparison | ``==`` | ``function (T, T) pure returns (bool)`` | +| +----------+---------------------------------------------+ +| | ``!=`` | ``function (T, T) pure returns (bool)`` | +| +----------+---------------------------------------------+ +| | ``<`` | ``function (T, T) pure returns (bool)`` | +| +----------+---------------------------------------------+ +| | ``<=`` | ``function (T, T) pure returns (bool)`` | +| +----------+---------------------------------------------+ +| | ``>`` | ``function (T, T) pure returns (bool)`` | +| +----------+---------------------------------------------+ +| | ``>=`` | ``function (T, T) pure returns (bool)`` | ++------------+----------+---------------------------------------------+ + +Note that unary and binary ``-`` need separate definitions. +The compiler will choose the right definition based on how the operator is invoked. The ``using A for B;`` directive is active only within the current scope (either the contract or the current module/source unit), @@ -46,7 +96,7 @@ outside of the contract or module in which it is used. When the directive is used at file level and applied to a user-defined type which was defined at file level in the same file, the word ``global`` can be added at the end. This will have the -effect that the functions are attached to the type everywhere +effect that the functions and operators are attached to the type everywhere the type is available (including other files), not only in the scope of the using statement. @@ -150,3 +200,37 @@ if you pass memory or value types, a copy will be performed, even in case of the ``self`` variable. The only situation where no copy will be performed is when storage reference variables are used or when internal library functions are called. + +Another example shows how to define a custom operator for a user-defined type: + +.. code-block:: solidity + + // SPDX-License-Identifier: GPL-3.0 + pragma solidity ^0.8.19; + + type UFixed16x2 is uint16; + + using { + add as +, + div as / + } for UFixed16x2 global; + + uint32 constant SCALE = 100; + + function add(UFixed16x2 a, UFixed16x2 b) pure returns (UFixed16x2) { + return UFixed16x2.wrap(UFixed16x2.unwrap(a) + UFixed16x2.unwrap(b)); + } + + function div(UFixed16x2 a, UFixed16x2 b) pure returns (UFixed16x2) { + uint32 a32 = UFixed16x2.unwrap(a); + uint32 b32 = UFixed16x2.unwrap(b); + uint32 result32 = a32 * SCALE / b32; + require(result32 <= type(uint16).max, "Divide overflow"); + return UFixed16x2.wrap(uint16(a32 * SCALE / b32)); + } + + contract Math { + function avg(UFixed16x2 a, UFixed16x2 b) public pure returns (UFixed16x2) { + return (a + b) / UFixed16x2.wrap(200); + } + } diff --git a/docs/contracts/visibility-and-getters.rst b/docs/contracts/visibility-and-getters.rst index 8932c5079b..5bf46dea55 100644 --- a/docs/contracts/visibility-and-getters.rst +++ b/docs/contracts/visibility-and-getters.rst @@ -200,12 +200,12 @@ The next example is more complex: struct Data { uint a; bytes3 b; - mapping (uint => uint) map; + mapping(uint => uint) map; uint[3] c; uint[] d; bytes e; } - mapping (uint => mapping(bool => Data[])) public data; + mapping(uint => mapping(bool => Data[])) public data; } It generates a function of the following form. The mapping and arrays (with the diff --git a/docs/contributing.rst b/docs/contributing.rst index da949c1369..bb88d12016 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -28,11 +28,11 @@ Team Calls ========== If you have issues or pull requests to discuss, or are interested in hearing what -the team and contributors are working on, you can join our public team calls: +the team and contributors are working on, you can join our public team call: -- Mondays and Wednesdays at 3PM CET/CEST. +- Wednesdays at 3PM CET/CEST. -Both calls take place on `Jitsi `_. +The call takes place on `Jitsi `_. How to Report Issues ==================== @@ -45,7 +45,7 @@ reporting issues, please mention the following details: * Source code (if applicable). * Operating system. * Steps to reproduce the issue. -* Actual vs. expected behaviour. +* Actual vs. expected behavior. Reducing the source code that caused the issue to a bare minimum is always very helpful, and sometimes even clarifies a misunderstanding. @@ -93,8 +93,8 @@ Prerequisites For running all compiler tests you may want to optionally install a few dependencies (`evmone `_, -`libz3 `_, and -`libhera `_). +`libz3 `_, `Eldarica `_, +`cvc5 `). On macOS systems, some of the testing scripts expect GNU coreutils to be installed. This can be easiest accomplished using Homebrew: ``brew install coreutils``. @@ -102,9 +102,9 @@ This can be easiest accomplished using Homebrew: ``brew install coreutils``. On Windows systems, make sure that you have a privilege to create symlinks, otherwise several tests may fail. Administrators should have that privilege, but you may also -`grant it to other users `_ +`grant it to other users `_ or -`enable Developer Mode `_. +`enable Developer Mode `_. Running the Tests ----------------- @@ -115,7 +115,7 @@ Running ``build/test/soltest`` or its wrapper ``scripts/soltest.sh`` is sufficie The ``./scripts/tests.sh`` script executes most Solidity tests automatically, including those bundled into the `Boost C++ Test Framework `_ -application ``soltest`` (or its wrapper ``scripts/soltest.sh``), as well as command line tests and +application ``soltest`` (or its wrapper ``scripts/soltest.sh``), as well as command-line tests and compilation tests. The test system automatically tries to discover the location of @@ -129,24 +129,21 @@ for the ``evmone`` shared object can be specified via the ``ETH_EVMONE`` environ If you do not have it installed, you can skip these tests by passing the ``--no-semantic-tests`` flag to ``scripts/soltest.sh``. -Running Ewasm tests is disabled by default and can be explicitly enabled -via ``./scripts/soltest.sh --ewasm`` and requires `hera `_ -to be found by ``soltest``. -The mechanism for locating the ``hera`` library is the same as for ``evmone``, except that the -variable for specifying an explicit location is called ``ETH_HERA``. - -The ``evmone`` and ``hera`` libraries should both end with the file name +The ``evmone`` library should end with the file name extension ``.so`` on Linux, ``.dll`` on Windows systems and ``.dylib`` on macOS. For running SMT tests, the ``libz3`` library must be installed and locatable by ``cmake`` during compiler configure stage. +A few SMT tests use ``Eldarica`` instead of ``Z3``. +``Eldarica`` is a runtime dependency, its executable (``eld``) must be present in ``PATH`` for the tests to pass. +However, if ``Eldarica`` is not found, these tests will be automatically skipped. If the ``libz3`` library is not installed on your system, you should disable the SMT tests by exporting ``SMT_FLAGS=--no-smt`` before running ``./scripts/tests.sh`` or running ``./scripts/soltest.sh --no-smt``. -These tests are ``libsolidity/smtCheckerTests`` and ``libsolidity/smtCheckerTestsJSON``. +These tests are ``libsolidity/smtCheckerTests``. -.. note :: +.. note:: To get a list of all unit tests run by Soltest, run ``./build/test/soltest --list_content=HRF``. @@ -167,7 +164,7 @@ See especially: - `run_test (-t) `_ to run specific tests cases, and - `report-level (-r) `_ give a more detailed report. -.. note :: +.. note:: Those working in a Windows environment wanting to run the above basic sets without libz3. Using Git Bash, you use: ``./build/test/Release/soltest.exe -- --no-smt``. @@ -175,6 +172,7 @@ See especially: If you want to debug using GDB, make sure you build differently than the "usual". For example, you could run the following command in your ``build`` folder: + .. code-block:: bash cmake -DCMAKE_BUILD_TYPE=Debug .. @@ -245,7 +243,7 @@ provides a way to edit, update or skip the current contract file, or quit the ap It offers several options for failing tests: -- ``edit``: ``isoltest`` tries to open the contract in an editor so you can adjust it. It either uses the editor given on the command line (as ``isoltest --editor /path/to/editor``), in the environment variable ``EDITOR`` or just ``/usr/bin/editor`` (in that order). +- ``edit``: ``isoltest`` tries to open the contract in an editor so you can adjust it. It either uses the editor given on the command-line (as ``isoltest --editor /path/to/editor``), in the environment variable ``EDITOR`` or just ``/usr/bin/editor`` (in that order). - ``update``: Updates the expectations for contract under test. This updates the annotations by removing unmet expectations and adding missing expectations. The test is then run again. - ``skip``: Skips the execution of this particular test. - ``quit``: Quits ``isoltest``. @@ -275,6 +273,62 @@ and re-run the test. It now passes again: Do not put more than one contract into a single file, unless you are testing inheritance or cross-contract calls. Each file should test one aspect of your new feature. +Command-line Tests +------------------ + +Our suite of end-to-end command-line tests checks the behaviour of the compiler binary as a whole +in various scenarios. +These tests are located in `test/cmdlineTests/ `_, +one per subdirectory, and can be executed using the ``cmdlineTests.sh`` script. + +By default the script runs all available tests. +You can also provide one or more `file name patterns `_, +in which case only the tests matching at least one pattern will be executed. +It is also possible to exclude files matching a specific pattern by prefixing it with ``--exclude``. + +By default the script assumes that a ``solc`` binary is available inside the ``build/`` subdirectory +inside the working copy. +If you build the compiler outside of the source tree, you can use the ``SOLIDITY_BUILD_DIR`` environment +variable to specify a different location for the build directory. + +Example: + +.. code-block:: bash + + export SOLIDITY_BUILD_DIR=~/solidity/build/ + test/cmdlineTests.sh "standard_*" "*_yul_*" --exclude "standard_yul_*" + +The commands above will run tests from directories starting with ``test/cmdlineTests/standard_`` and +subdirectories of ``test/cmdlineTests/`` that have ``_yul_`` somewhere in the name, +but no test whose name starts with ``standard_yul_`` will be executed. +It will also assume that the file ``solidity/build/solc/solc`` inside your home directory is the +compiler binary (unless you are on Windows -- then ``solidity/build/solc/Release/solc.exe``). + +There are several kinds of command-line tests: + +- *Standard JSON test*: contains at least an ``input.json`` file. + In general may contain: + + - ``input.json``: input file to be passed to the ``--standard-json`` option on the command line. + - ``output.json``: expected Standard JSON output. + - ``args``: extra command-line arguments passed to ``solc``. + +- *CLI test*: contains at least an ``input.*`` file (other than ``input.json``). + In general may contain: + + - ``input.*``: a single input file, whose name will be supplied to ``solc`` on the command line. + Usually ``input.sol`` or ``input.yul``. + - ``args``: extra command-line arguments passed to ``solc``. + - ``stdin``: content to be passed to ``solc`` via standard input. + - ``output``: expected content of the standard output. + - ``err``: expected content of the standard error output. + - ``exit``: expected exit code. If not provided, zero is expected. + +- *Script test*: contains a ``test.*`` file. + In general may contain: + + - ``test.*``: a single script to run, usually ``test.sh`` or ``test.py``. + The script must be executable. Running the Fuzzer via AFL ========================== @@ -356,7 +410,7 @@ The AFL documentation states that the corpus (the initial input files) should no too large. The files themselves should not be larger than 1 kB and there should be at most one input file per functionality, so better start with a small number of. There is also a tool called ``afl-cmin`` that can trim input files -that result in similar behaviour of the binary. +that result in similar behavior of the binary. Now run the fuzzer (the ``-m`` extends the size of memory to 60 MB): @@ -403,13 +457,12 @@ contributions to Solidity. English Language ---------------- -Use English, with British English spelling preferred, unless using project or brand names. Try to reduce the usage of -local slang and references, making your language as clear to all readers as possible. Below are some references to help: +Use International English, unless using project or brand names. Try to reduce the usage of +local slang and references, making your language as clear to all readers as possible. +Below are some references to help: * `Simplified technical English `_ * `International English `_ -* `British English spelling `_ - .. note:: @@ -503,7 +556,7 @@ topics, issues or feature implementations are debated in detail. The invitation We are also sharing feedback surveys and other content that is relevant to language design in the forum. -If you want to know where the team is standing in terms or implementing new features, you can follow the implementation status in the `Solidity Github project `_. +If you want to know where the team is standing in terms of implementing new features, you can follow the implementation status in the `Solidity GitHub project `_. Issues in the design backlog need further specification and will either be discussed in a language design call or in a regular team call. You can see the upcoming changes for the next breaking release by changing from the default branch (`develop`) to the `breaking branch `_. diff --git a/docs/control-structures.rst b/docs/control-structures.rst index 18a522217f..238d261152 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -173,7 +173,6 @@ parameters from the function declaration, but can be in arbitrary order. function set(uint key, uint value) public { data[key] = value; } - } Omitted Names in Function Definitions @@ -366,13 +365,13 @@ i.e. the following is not valid: ``(x, uint y) = (1, 2);`` .. warning:: Be careful when assigning to multiple variables at the same time when reference types are involved, because it could lead to unexpected - copying behaviour. + copying behavior. Complications for Arrays and Structs ------------------------------------ The semantics of assignments are more complicated for non-value types like arrays and structs, -including ``bytes`` and ``string``, see :ref:`Data location and assignment behaviour ` for details. +including ``bytes`` and ``string``, see :ref:`Data location and assignment behavior ` for details. In the example below the call to ``g(x)`` has no effect on ``x`` because it creates an independent copy of the storage value in memory. However, ``h(x)`` successfully modifies ``x`` @@ -511,7 +510,7 @@ additional checks. Since Solidity 0.8.0, all arithmetic operations revert on over- and underflow by default, thus making the use of these libraries unnecessary. -To obtain the previous behaviour, an ``unchecked`` block can be used: +To obtain the previous behavior, an ``unchecked`` block can be used: .. code-block:: solidity @@ -595,6 +594,8 @@ The built-in errors ``Error(string)`` and ``Panic(uint256)`` are used by special functions, as explained below. ``Error`` is used for "regular" error conditions while ``Panic`` is used for errors that should not be present in bug-free code. +.. _assert-and-require-statements: + Panic via ``assert`` and Error via ``require`` ---------------------------------------------- @@ -626,21 +627,20 @@ The error code supplied with the error data indicates the kind of panic. #. 0x41: If you allocate too much memory or create an array that is too large. #. 0x51: If you call a zero-initialized variable of internal function type. -The ``require`` function either creates an error without any data or -an error of type ``Error(string)``. It -should be used to ensure valid conditions -that cannot be detected until execution time. -This includes conditions on inputs -or return values from calls to external contracts. +The ``require`` function provides three overloads: -.. note:: +1. ``require(bool)`` which will revert without any data (not even an error selector). +2. ``require(bool, string)`` which will revert with an ``Error(string)``. +3. ``require(bool, error)`` which will revert with the custom, user supplied error provided as the second argument. - It is currently not possible to use custom errors in combination - with ``require``. Please use ``if (!condition) revert CustomError();`` instead. +.. note:: + ``require`` arguments are evaluated unconditionally, so take special care to make sure that + they are not expressions with unexpected side-effects. + For example, in ``require(condition, CustomError(f()));`` and ``require(condition, f());``, + function ``f()`` will be called regardless of whether the supplied condition is ``true`` or ``false``. An ``Error(string)`` exception (or an exception without data) is generated -by the compiler -in the following situations: +by the compiler in the following situations: #. Calling ``require(x)`` where ``x`` evaluates to ``false``. #. If you use ``revert()`` or ``revert("description")``. @@ -651,7 +651,7 @@ in the following situations: For the following cases, the error data from the external call (if provided) is forwarded. This means that it can either cause -an `Error` or a `Panic` (or whatever else was given): +an ``Error`` or a ``Panic`` (or whatever else was given): #. If a ``.transfer()`` fails. #. If you call a function via a message call but it does not finish @@ -663,10 +663,10 @@ an `Error` or a `Panic` (or whatever else was given): #. If you create a contract using the ``new`` keyword but the contract creation :ref:`does not finish properly`. -You can optionally provide a message string for ``require``, but not for ``assert``. +You can optionally provide a message string or a custom error to ``require``, but not to ``assert``. .. note:: - If you do not provide a string argument to ``require``, it will revert + If you do not provide a string or custom error argument to ``require``, it will revert with empty error data, not even including the error selector. @@ -686,7 +686,7 @@ and ``assert`` for internal error checking. addr.transfer(msg.value / 2); // Since transfer throws an exception on failure and // cannot call back here, there should be no way for us to - // still have half of the money. + // still have half of the Ether. assert(address(this).balance == balanceBeforeTransfer - msg.value / 2); return address(this).balance; } @@ -720,7 +720,7 @@ The ``revert`` statement takes a custom error as direct argument without parenth revert CustomError(arg1, arg2); -For backwards-compatibility reasons, there is also the ``revert()`` function, which uses parentheses +For backward-compatibility reasons, there is also the ``revert()`` function, which uses parentheses and accepts a string: revert(); diff --git a/docs/docs.sh b/docs/docs.sh index f2c5667612..abe4921e30 100755 --- a/docs/docs.sh +++ b/docs/docs.sh @@ -31,5 +31,8 @@ set -euo pipefail script_dir="$(dirname "$0")" cd "${script_dir}" -pip3 install -r requirements.txt --upgrade --upgrade-strategy eager +# TODO `--ignore-installed` now fixes an issue where pip tries to uninstall a Debian installed package, but is unable to +# TODO since Debian has decided to not use the RECORD file, which then breaks pip. +# TODO https://github.com/pypa/pip/issues/11631 and https://bugs.launchpad.net/ubuntu/+source/wheel/+bug/2063151 +pip3 install -r requirements.txt --ignore-installed --upgrade --upgrade-strategy eager sphinx-build -nW -b html -d _build/doctrees . _build/html diff --git a/docs/examples/blind-auction.rst b/docs/examples/blind-auction.rst index 3c7b3dcc69..47e19033f9 100644 --- a/docs/examples/blind-auction.rst +++ b/docs/examples/blind-auction.rst @@ -16,11 +16,11 @@ Simple Open Auction =================== The general idea of the following simple auction contract is that everyone can -send their bids during a bidding period. The bids already include sending money -/ Ether in order to bind the bidders to their bid. If the highest bid is -raised, the previous highest bidder gets their money back. After the end of +send their bids during a bidding period. The bids already include sending some compensation, +e.g. Ether, in order to bind the bidders to their bid. If the highest bid is +raised, the previous highest bidder gets their Ether back. After the end of the bidding period, the contract has to be called manually for the beneficiary -to receive their money - contracts cannot activate themselves. +to receive their Ether - contracts cannot activate themselves. .. code-block:: solidity @@ -92,19 +92,19 @@ to receive their money - contracts cannot activate themselves. revert AuctionAlreadyEnded(); // If the bid is not higher, send the - // money back (the revert statement + // Ether back (the revert statement // will revert all changes in this // function execution including - // it having received the money). + // it having received the Ether). if (msg.value <= highestBid) revert BidNotHighEnough(highestBid); if (highestBid != 0) { - // Sending back the money by simply using + // Sending back the Ether by simply using // highestBidder.send(highestBid) is a security risk // because it could execute an untrusted contract. // It is always safer to let the recipients - // withdraw their money themselves. + // withdraw their Ether themselves. pendingReturns[highestBidder] += highestBid; } highestBidder = msg.sender; @@ -182,7 +182,7 @@ the contract checks that the hash value is the same as the one provided during the bidding period. Another challenge is how to make the auction **binding and blind** at the same -time: The only way to prevent the bidder from just not sending the money after +time: The only way to prevent the bidder from just not sending the Ether after they won the auction is to make them send it together with the bid. Since value transfers cannot be blinded in Ethereum, anyone can see the value. diff --git a/docs/examples/micropayment.rst b/docs/examples/micropayment.rst index 7ade926572..9a3ad845ce 100644 --- a/docs/examples/micropayment.rst +++ b/docs/examples/micropayment.rst @@ -36,7 +36,7 @@ Creating the signature Alice does not need to interact with the Ethereum network to sign the transaction, the process is completely offline. In this tutorial, we will sign messages in the browser -using `web3.js `_ and +using `web3.js `_ and `MetaMask `_, using the method described in `EIP-712 `_, as it provides a number of other security benefits. @@ -55,7 +55,7 @@ as it provides a number of other security benefits. What to Sign ------------ -For a contract that fulfils payments, the signed message must include: +For a contract that fulfills payments, the signed message must include: 1. The recipient's address. 2. The amount to be transferred. @@ -80,13 +80,17 @@ the contract's address itself will be accepted. You can find an example of this in the first two lines of the ``claimPayment()`` function of the full contract at the end of this section. +Furthermore, instead of destroying the contract by calling ``selfdestruct``, +which is currently deprecated, we will disable the contract's functionalities by freezing it, +resulting in the reversion of any call after it being frozen. + Packing arguments ----------------- Now that we have identified what information to include in the signed message, we are ready to put the message together, hash it, and sign it. For simplicity, we concatenate the data. The `ethereumjs-abi `_ -library provides a function called ``soliditySHA3`` that mimics the behaviour of +library provides a function called ``soliditySHA3`` that mimics the behavior of Solidity's ``keccak256`` function applied to arguments encoded using ``abi.encodePacked``. Here is a JavaScript function that creates the proper signature for the ``ReceiverPays`` example: @@ -144,29 +148,54 @@ The full contract // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.7.0 <0.9.0; - contract ReceiverPays { - address owner = msg.sender; + contract Owned { + address payable owner; + constructor() { + owner = payable(msg.sender); + } + } + + contract Freezable is Owned { + bool private _frozen = false; + + modifier notFrozen() { + require(!_frozen, "Inactive Contract."); + _; + } + + function freeze() internal { + if (msg.sender == owner) + _frozen = true; + } + } + + contract ReceiverPays is Freezable { mapping(uint256 => bool) usedNonces; constructor() payable {} - function claimPayment(uint256 amount, uint256 nonce, bytes memory signature) external { + function claimPayment(uint256 amount, uint256 nonce, bytes memory signature) + external + notFrozen + { require(!usedNonces[nonce]); usedNonces[nonce] = true; // this recreates the message that was signed on the client bytes32 message = prefixed(keccak256(abi.encodePacked(msg.sender, amount, nonce, this))); - require(recoverSigner(message, signature) == owner); - payable(msg.sender).transfer(amount); } - /// destroy the contract and reclaim the leftover funds. - function shutdown() external { + /// freeze the contract and reclaim the leftover funds. + function shutdown() + external + notFrozen + { require(msg.sender == owner); - selfdestruct(payable(msg.sender)); + freeze(); + payable(msg.sender).transfer(address(this).balance); } /// signature methods. @@ -195,7 +224,6 @@ The full contract returns (address) { (uint8 v, bytes32 r, bytes32 s) = splitSignature(sig); - return ecrecover(message, v, r, s); } @@ -304,7 +332,7 @@ Closing the Payment Channel When Bob is ready to receive his funds, it is time to close the payment channel by calling a ``close`` function on the smart contract. Closing the channel pays the recipient the Ether they are owed and -destroys the contract, sending any remaining Ether back to Alice. To +deactivates the contract by freezing it, sending any remaining Ether back to Alice. To close the channel, Bob needs to provide a message signed by Alice. The smart contract must verify that the message contains a valid signature from the sender. @@ -319,7 +347,7 @@ they could provide a message with a lower amount and cheat the recipient out of The function verifies the signed message matches the given parameters. If everything checks out, the recipient is sent their portion of the Ether, -and the sender is sent the rest via a ``selfdestruct``. +and the sender is sent the remaining funds via a ``transfer``. You can see the ``close`` function in the full contract. Channel Expiration @@ -341,10 +369,24 @@ The full contract // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.7.0 <0.9.0; - contract SimplePaymentChannel { - address payable public sender; // The account sending payments. - address payable public recipient; // The account receiving the payments. - uint256 public expiration; // Timeout in case the recipient never closes. + + contract Frozeable { + bool private _frozen = false; + + modifier notFrozen() { + require(!_frozen, "Inactive Contract."); + _; + } + + function freeze() internal { + _frozen = true; + } + } + + contract SimplePaymentChannel is Frozeable { + address payable public sender; // The account sending payments. + address payable public recipient; // The account receiving the payments. + uint256 public expiration; // Timeout in case the recipient never closes. constructor (address payable recipientAddress, uint256 duration) payable @@ -357,16 +399,23 @@ The full contract /// the recipient can close the channel at any time by presenting a /// signed amount from the sender. the recipient will be sent that amount, /// and the remainder will go back to the sender - function close(uint256 amount, bytes memory signature) external { + function close(uint256 amount, bytes memory signature) + external + notFrozen + { require(msg.sender == recipient); require(isValidSignature(amount, signature)); recipient.transfer(amount); - selfdestruct(sender); + freeze(); + sender.transfer(address(this).balance); } /// the sender can extend the expiration at any time - function extend(uint256 newExpiration) external { + function extend(uint256 newExpiration) + external + notFrozen + { require(msg.sender == sender); require(newExpiration > expiration); @@ -375,9 +424,13 @@ The full contract /// if the timeout is reached without the recipient closing the channel, /// then the Ether is released back to the sender. - function claimTimeout() external { + function claimTimeout() + external + notFrozen + { require(block.timestamp >= expiration); - selfdestruct(sender); + freeze(); + sender.transfer(address(this).balance); } function isValidSignature(uint256 amount, bytes memory signature) @@ -386,14 +439,12 @@ The full contract returns (bool) { bytes32 message = prefixed(keccak256(abi.encodePacked(this, amount))); - // check that the signature is from the payment sender return recoverSigner(message, signature) == sender; } /// All functions below this are just taken from the chapter /// 'creating and verifying signatures' chapter. - function splitSignature(bytes memory sig) internal pure @@ -409,7 +460,6 @@ The full contract // final byte (first byte of the next 32 bytes) v := byte(0, mload(add(sig, 96))) } - return (v, r, s); } @@ -419,7 +469,6 @@ The full contract returns (address) { (uint8 v, bytes32 r, bytes32 s) = splitSignature(sig); - return ecrecover(message, v, r, s); } @@ -433,7 +482,7 @@ The full contract .. note:: The function ``splitSignature`` does not use all security checks. A real implementation should use a more rigorously tested library, - such as openzepplin's `version `_ of this code. + such as openzeppelin's `version `_ of this code. Verifying Payments ------------------ diff --git a/docs/examples/modular.rst b/docs/examples/modular.rst index 697699ae6e..9ae2849cb4 100644 --- a/docs/examples/modular.rst +++ b/docs/examples/modular.rst @@ -7,7 +7,7 @@ Modular Contracts A modular approach to building your contracts helps you reduce the complexity and improve the readability which will help to identify bugs and vulnerabilities during development and code review. -If you specify and control the behaviour of each module in isolation, the +If you specify and control the behavior of each module in isolation, the interactions you have to consider are only those between the module specifications and not every other moving part of the contract. In the example below, the contract uses the ``move`` method @@ -34,7 +34,7 @@ and the sum of all balances is an invariant across the lifetime of the contract. contract Token { mapping(address => uint256) balances; using Balances for *; - mapping(address => mapping (address => uint256)) allowed; + mapping(address => mapping(address => uint256)) allowed; event Transfer(address from, address to, uint amount); event Approval(address owner, address spender, uint amount); diff --git a/docs/examples/safe-remote.rst b/docs/examples/safe-remote.rst index e42b017fb6..a2651af238 100644 --- a/docs/examples/safe-remote.rst +++ b/docs/examples/safe-remote.rst @@ -6,18 +6,18 @@ Safe Remote Purchase Purchasing goods remotely currently requires multiple parties that need to trust each other. The simplest configuration involves a seller and a buyer. The buyer would like to receive -an item from the seller and the seller would like to get money (or an equivalent) +an item from the seller and the seller would like to get some compensation, e.g. Ether, in return. The problematic part is the shipment here: There is no way to determine for sure that the item arrived at the buyer. There are multiple ways to solve this problem, but all fall short in one or the other way. In the following example, both parties have to put twice the value of the item into the -contract as escrow. As soon as this happened, the money will stay locked inside +contract as escrow. As soon as this happened, the Ether will stay locked inside the contract until the buyer confirms that they received the item. After that, the buyer is returned the value (half of their deposit) and the seller gets three times the value (their deposit plus the value). The idea behind this is that both parties have an incentive to resolve the situation or otherwise -their money is locked forever. +their Ether is locked forever. This contract of course does not solve the problem, but gives an overview of how you can use state machine-like constructs inside a contract. diff --git a/docs/ext/remix_code_links.py b/docs/ext/remix_code_links.py index 2fc15ddda0..55fc0ef5c2 100644 --- a/docs/ext/remix_code_links.py +++ b/docs/ext/remix_code_links.py @@ -22,23 +22,16 @@ def remix_code_url(source_code, language, solidity_version): # NOTE: base64 encoded data may contain +, = and / characters. Remix seems to handle them just # fine without any escaping. base64_encoded_source = base64.b64encode(source_code.encode('utf-8')).decode('ascii') - return f"https://remix.ethereum.org/?language={language}&version={solidity_version}&code={base64_encoded_source}" + return f"https://remix.ethereum.org/?#language={language}&version={solidity_version}&code={base64_encoded_source}" def build_remix_link_node(url): - link_icon_node = docutils.nodes.inline() - link_icon_node.set_class('link-icon') - - link_text_node = docutils.nodes.inline(text="open in Remix") - link_text_node.set_class('link-text') - - reference_node = docutils.nodes.reference('', '', internal=False, refuri=url) - reference_node.set_class('remix-link') - reference_node += [link_icon_node, link_text_node] + reference_node = docutils.nodes.reference('', 'open in Remix', internal=False, refuri=url, target='_blank') + reference_node['classes'].append('remix-link') paragraph_node = docutils.nodes.paragraph() - paragraph_node.set_class('remix-link-container') - paragraph_node += reference_node + paragraph_node['classes'].append('remix-link-container') + paragraph_node.append(reference_node) return paragraph_node @@ -49,22 +42,24 @@ def insert_remix_link(app, doctree, solidity_version): for literal_block_node in doctree.traverse(docutils.nodes.literal_block): assert 'language' in literal_block_node.attributes language = literal_block_node.attributes['language'].lower() - if language in ['solidity', 'yul']: - text_nodes = list(literal_block_node.traverse(docutils.nodes.Text)) - assert len(text_nodes) == 1 - - remix_url = remix_code_url(text_nodes[0], language, solidity_version) - url_length = len(remix_url.encode('utf-8')) - if url_length > MAX_SAFE_URL_LENGTH: - logger.warning( - "Remix URL generated from the code snippet exceeds the maximum safe URL length " - " (%d > %d bytes).", - url_length, - MAX_SAFE_URL_LENGTH, - location=(literal_block_node.source, literal_block_node.line), - ) - - insert_node_before(literal_block_node, build_remix_link_node(remix_url)) + if language not in ['solidity', 'yul']: + continue + + text_nodes = list(literal_block_node.traverse(docutils.nodes.Text)) + assert len(text_nodes) == 1 + + remix_url = remix_code_url(text_nodes[0], language, solidity_version) + url_length = len(remix_url.encode('utf-8')) + if url_length > MAX_SAFE_URL_LENGTH: + logger.warning( + "Remix URL generated from the code snippet exceeds the maximum safe URL length " + " (%d > %d bytes).", + url_length, + MAX_SAFE_URL_LENGTH, + location=(literal_block_node.source, literal_block_node.line), + ) + + insert_node_before(literal_block_node, build_remix_link_node(remix_url)) def setup(app): diff --git a/docs/grammar/SolidityLexer.g4 b/docs/grammar/SolidityLexer.g4 index c47dc411cd..f994c331d5 100644 --- a/docs/grammar/SolidityLexer.g4 +++ b/docs/grammar/SolidityLexer.g4 @@ -62,7 +62,7 @@ New: 'new'; /** * Unit denomination for numbers. */ -NumberUnit: 'wei' | 'gwei' | 'ether' | 'seconds' | 'minutes' | 'hours' | 'days' | 'weeks' | 'years'; +SubDenomination: 'wei' | 'gwei' | 'ether' | 'seconds' | 'minutes' | 'hours' | 'days' | 'weeks' | 'years'; Override: 'override'; Payable: 'payable'; Pragma: 'pragma' -> pushMode(PragmaMode); @@ -85,11 +85,13 @@ SignedIntegerType: Storage: 'storage'; String: 'string'; Struct: 'struct'; +Transient: 'transient'; // not a real keyword True: 'true'; Try: 'try'; Type: 'type'; Ufixed: 'ufixed' | ('ufixed' [1-9][0-9]+ 'x' [1-9][0-9]+); Unchecked: 'unchecked'; +Unicode: 'unicode'; /** * Sized unsigned integer types. * uint is an alias of uint256. @@ -198,9 +200,7 @@ fragment EscapeSequence: /** * A single quoted string literal allowing arbitrary unicode characters. */ -UnicodeStringLiteral: - 'unicode"' DoubleQuotedUnicodeStringCharacter* '"' - | 'unicode\'' SingleQuotedUnicodeStringCharacter* '\''; +UnicodeStringLiteral: 'unicode' (('"' DoubleQuotedUnicodeStringCharacter* '"') | ('\'' SingleQuotedUnicodeStringCharacter* '\'')); //@doc:inline fragment DoubleQuotedUnicodeStringCharacter: ~["\r\n\\] | EscapeSequence; //@doc:inline @@ -222,6 +222,14 @@ fragment EvenHexDigits: HexCharacter HexCharacter ('_'? HexCharacter HexCharacte //@doc:inline fragment HexCharacter: [0-9A-Fa-f]; +/** + * Scanned but not used by any rule, i.e, disallowed. + * solc parser considers number starting with '0', not immediately followed by '.' or 'x' as + * octal, even if non octal digits '8' and '9' are present. + */ +OctalNumber: '0' DecimalDigits ('.' DecimalDigits)?; + + /** * A decimal number literal consists of decimal digits that may be delimited by underscores and * an optional positive or negative exponent. @@ -232,6 +240,12 @@ DecimalNumber: (DecimalDigits | (DecimalDigits? '.' DecimalDigits)) ([eE] '-'? D fragment DecimalDigits: [0-9] ('_'? [0-9])* ; +/** + * This is needed to avoid successfully parsing a number followed by a string with no whitespace between. + */ +DecimalNumberFollowedByIdentifier: DecimalNumber Identifier; + + /** * An identifier in solidity has to start with a letter, a dollar-sign or an underscore and * may additionally contain numbers after the first symbol. @@ -285,14 +299,14 @@ YulEVMBuiltin: 'stop' | 'add' | 'sub' | 'mul' | 'div' | 'sdiv' | 'mod' | 'smod' | 'exp' | 'not' | 'lt' | 'gt' | 'slt' | 'sgt' | 'eq' | 'iszero' | 'and' | 'or' | 'xor' | 'byte' | 'shl' | 'shr' | 'sar' | 'addmod' | 'mulmod' | 'signextend' | 'keccak256' - | 'pop' | 'mload' | 'mstore' | 'mstore8' | 'sload' | 'sstore' | 'msize' | 'gas' + | 'pop' | 'mload' | 'mstore' | 'mstore8' | 'sload' | 'sstore' | 'tload' | 'tstore'| 'msize' | 'gas' | 'address' | 'balance' | 'selfbalance' | 'caller' | 'callvalue' | 'calldataload' | 'calldatasize' | 'calldatacopy' | 'extcodesize' | 'extcodecopy' | 'returndatasize' - | 'returndatacopy' | 'extcodehash' | 'create' | 'create2' | 'call' | 'callcode' + | 'returndatacopy' | 'mcopy' | 'extcodehash' | 'create' | 'create2' | 'call' | 'callcode' | 'delegatecall' | 'staticcall' | 'return' | 'revert' | 'selfdestruct' | 'invalid' | 'log0' | 'log1' | 'log2' | 'log3' | 'log4' | 'chainid' | 'origin' | 'gasprice' - | 'blockhash' | 'coinbase' | 'timestamp' | 'number' | 'difficulty' | 'gaslimit' - | 'basefee'; + | 'blockhash' | 'blobhash' | 'coinbase' | 'timestamp' | 'number' | 'difficulty' + | 'prevrandao' | 'gaslimit' | 'basefee' | 'blobbasefee'; YulLBrace: '{' -> pushMode(YulMode); YulRBrace: '}' -> popMode; diff --git a/docs/grammar/SolidityParser.g4 b/docs/grammar/SolidityParser.g4 index 3068749895..a344fad544 100644 --- a/docs/grammar/SolidityParser.g4 +++ b/docs/grammar/SolidityParser.g4 @@ -22,6 +22,7 @@ sourceUnit: ( | enumDefinition | userDefinedValueTypeDefinition | errorDefinition + | eventDefinition )* EOF; //@doc: inline @@ -152,7 +153,7 @@ stateMutability: Pure | View | Payable; */ overrideSpecifier: Override (LParen overrides+=identifierPath (Comma overrides+=identifierPath)* RParen)?; /** - * The definition of contract, library and interface functions. + * The definition of contract, library, interface or free functions. * Depending on the context in which the function is defined, further restrictions may apply, * e.g. functions in interfaces have to be unimplemented, i.e. may not contain a body block. */ @@ -161,7 +162,7 @@ locals[ boolean visibilitySet = false, boolean mutabilitySet = false, boolean virtualSet = false, - boolean overrideSpecifierSet = false + boolean overrideSpecifierSet = false, ] : Function (identifier | Fallback | Receive) @@ -175,6 +176,7 @@ locals[ )* (Returns LParen returnParameters=parameterList RParen)? (Semicolon | body=block); + /** * The definition of a modifier. * Note that within the body block of a modifier, the underscore cannot be used as identifier, @@ -249,7 +251,7 @@ structMember: type=typeName name=identifier Semicolon; /** * Definition of an enum. Can occur at top-level within a source unit or within a contract, library or interface. */ -enumDefinition: Enum name=identifier LBrace enumValues+=identifier (Comma enumValues+=identifier)* RBrace; +enumDefinition: Enum name=identifier LBrace enumValues+=identifier (Comma enumValues+=identifier)* RBrace; /** * Definition of a user defined value type. Can occur at top-level within a source unit or within a contract, library or interface. */ @@ -260,7 +262,7 @@ userDefinedValueTypeDefinition: * The declaration of a state variable. */ stateVariableDeclaration -locals [boolean constantnessSet = false, boolean visibilitySet = false, boolean overrideSpecifierSet = false] +locals [boolean constantnessSet = false, boolean visibilitySet = false, boolean overrideSpecifierSet = false, boolean locationSet = false] : type=typeName ( @@ -270,6 +272,7 @@ locals [boolean constantnessSet = false, boolean visibilitySet = false, boolean | {!$constantnessSet}? Constant {$constantnessSet = true;} | {!$overrideSpecifierSet}? overrideSpecifier {$overrideSpecifierSet = true;} | {!$constantnessSet}? Immutable {$constantnessSet = true;} + | {!$locationSet}? Transient {$locationSet = true;} )* name=identifier (Assign initialValue=expression)? @@ -311,11 +314,38 @@ errorDefinition: LParen (parameters+=errorParameter (Comma parameters+=errorParameter)*)? RParen Semicolon; +/** + * Operators that users are allowed to implement for some types with `using for`. + */ +userDefinableOperator: + BitAnd + | BitNot + | BitOr + | BitXor + | Add + | Div + | Mod + | Mul + | Sub + | Equal + | GreaterThan + | GreaterThanOrEqual + | LessThan + | LessThanOrEqual + | NotEqual; + /** * Using directive to attach library functions and free functions to types. * Can occur within contracts and libraries and at the file level. */ -usingDirective: Using (identifierPath | (LBrace identifierPath (Comma identifierPath)* RBrace)) For (Mul | typeName) Global? Semicolon; +usingDirective: + Using ( + identifierPath + | (LBrace usingAliases (Comma usingAliases)* RBrace) + ) For (Mul | typeName) Global? Semicolon; + +usingAliases: identifierPath (As userDefinableOperator)?; + /** * A type name can be an elementary type, a function type, a mapping type, a user-defined type * (e.g. a contract or struct) or an array type. @@ -347,7 +377,7 @@ dataLocation: Memory | Storage | Calldata; */ expression: expression LBrack index=expression? RBrack # IndexAccess - | expression LBrack start=expression? Colon end=expression? RBrack # IndexRangeAccess + | expression LBrack startIndex=expression? Colon endIndex=expression? RBrack # IndexRangeAccess | expression Period (identifier | Address) # MemberAccess | expression LBrace (namedArgument (Comma namedArgument)*)? RBrace # FunctionCallOptions | expression callArgumentList # FunctionCall @@ -368,12 +398,13 @@ expression: | expression Or expression # OrOperation | expression Conditional expression Colon expression # Conditional | expression assignOp expression # Assignment - | New typeName # NewExpression + | New typeName # NewExpr | tupleExpression # Tuple | inlineArrayExpression # InlineArray - | ( + | ( identifier | literal + | literalWithSubDenomination | elementaryTypeName[false] ) # PrimaryExpression ; @@ -389,9 +420,12 @@ inlineArrayExpression: LBrack (expression ( Comma expression)* ) RBrack; /** * Besides regular non-keyword Identifiers, some keywords like 'from' and 'error' can also be used as identifiers. */ -identifier: Identifier | From | Error | Revert | Global; +identifier: Identifier | From | Error | Revert | Global | Transient; literal: stringLiteral | numberLiteral | booleanLiteral | hexStringLiteral | unicodeStringLiteral; + +literalWithSubDenomination: numberLiteral SubDenomination; + booleanLiteral: True | False; /** * A full string literal consists of either one or several consecutive quoted strings. @@ -409,7 +443,8 @@ unicodeStringLiteral: UnicodeStringLiteral+; /** * Number literals can be decimal or hexadecimal numbers with an optional unit. */ -numberLiteral: (DecimalNumber | HexNumber) NumberUnit?; +numberLiteral: DecimalNumber | HexNumber; + /** * A curly-braced block of statements. Opens its own scope. */ @@ -504,7 +539,7 @@ variableDeclarationTuple: variableDeclarationStatement: ((variableDeclaration (Assign expression)?) | (variableDeclarationTuple Assign expression)) Semicolon; expressionStatement: expression Semicolon; -mappingType: Mapping LParen key=mappingKeyType DoubleArrow value=typeName RParen; +mappingType: Mapping LParen key=mappingKeyType name=identifier? DoubleArrow value=typeName name=identifier? RParen; /** * Only elementary types or user defined types are viable as mapping keys. */ diff --git a/docs/index.rst b/docs/index.rst index 3cb27eaeda..f1608890d7 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,38 +1,35 @@ Solidity ======== -Solidity is an object-oriented, high-level language for implementing smart -contracts. Smart contracts are programs which govern the behaviour of accounts -within the Ethereum state. +Solidity is an object-oriented, high-level language for implementing smart contracts. +Smart contracts are programs that govern the behavior of accounts within the Ethereum state. -Solidity is a `curly-bracket language `_ designed to target the Ethereum Virtual Machine (EVM). -It is influenced by C++, Python and JavaScript. You can find more details about which languages Solidity has been inspired by in the :doc:`language influences ` section. +Solidity is a `curly-bracket language `_ designed to target the Ethereum Virtual Machine (EVM). +It is influenced by C++, Python, and JavaScript. +You can find more details about which languages Solidity has been inspired by in the :doc:`language influences ` section. -Solidity is statically typed, supports inheritance, libraries and complex -user-defined types among other features. +Solidity is statically typed, supports inheritance, libraries, and complex user-defined types, among other features. -With Solidity you can create contracts for uses such as voting, crowdfunding, blind auctions, -and multi-signature wallets. +With Solidity, you can create contracts for uses such as voting, crowdfunding, blind auctions, and multi-signature wallets. -When deploying contracts, you should use the latest released -version of Solidity. Apart from exceptional cases, only the latest version receives +When deploying contracts, you should use the latest released version of Solidity. +Apart from exceptional cases, only the latest version receives `security fixes `_. -Furthermore, breaking changes as well as -new features are introduced regularly. We currently use -a 0.y.z version number `to indicate this fast pace of change `_. +Furthermore, breaking changes, as well as new features, are introduced regularly. +We currently use a 0.y.z version number `to indicate this fast pace of change `_. .. warning:: - Solidity recently released the 0.8.x version that introduced a lot of breaking - changes. Make sure you read :doc:`the full list <080-breaking-changes>`. + Solidity recently released the 0.8.x version that introduced a lot of breaking changes. + Make sure you read :doc:`the full list <080-breaking-changes>`. Ideas for improving Solidity or this documentation are always welcome, read our :doc:`contributors guide ` for more details. .. Hint:: - You can download this documentation as PDF, HTML or Epub by clicking on the versions - flyout menu in the bottom-left corner and selecting the preferred download format. + You can download this documentation as PDF, HTML or Epub + by clicking on the versions flyout menu in the bottom-left corner and selecting the preferred download format. Getting Started @@ -40,8 +37,7 @@ Getting Started **1. Understand the Smart Contract Basics** -If you are new to the concept of smart contracts we recommend you to get started by digging -into the "Introduction to Smart Contracts" section, which covers: +If you are new to the concept of smart contracts, we recommend you to get started by digging into the "Introduction to Smart Contracts" section, which covers the following: * :ref:`A simple example smart contract ` written in Solidity. * :ref:`Blockchain Basics `. @@ -59,29 +55,27 @@ simply choose your preferred option and follow the steps outlined on the :ref:`i .. hint:: You can try out code examples directly in your browser with the - `Remix IDE `_. Remix is a web browser based IDE - that allows you to write, deploy and administer Solidity smart contracts, without - the need to install Solidity locally. + `Remix IDE `_. + Remix is a web browser-based IDE that allows you to write, deploy and administer Solidity smart contracts, + without the need to install Solidity locally. .. warning:: - As humans write software, it can have bugs. You should follow established - software development best-practices when writing your smart contracts. This - includes code review, testing, audits, and correctness proofs. Smart contract - users are sometimes more confident with code than their authors, and - blockchains and smart contracts have their own unique issues to - watch out for, so before working on production code, make sure you read the - :ref:`security_considerations` section. + As humans write software, it can have bugs. + Therefore, you should follow established software development best practices when writing your smart contracts. + This includes code review, testing, audits, and correctness proofs. + Smart contract users are sometimes more confident with code than their authors, + and blockchains and smart contracts have their own unique issues to watch out for, + so before working on production code, make sure you read the :ref:`security_considerations` section. **4. Learn More** -If you want to learn more about building decentralized applications on Ethereum, the -`Ethereum Developer Resources `_ -can help you with further general documentation around Ethereum, and a wide selection of tutorials, -tools and development frameworks. +If you want to learn more about building decentralized applications on Ethereum, +the `Ethereum Developer Resources `_ can help you with further general documentation around Ethereum, +and a wide selection of tutorials, tools, and development frameworks. If you have any questions, you can try searching for answers or asking on the -`Ethereum StackExchange `_, or -our `Gitter channel `_. +`Ethereum StackExchange `_, +or our `Gitter channel `_. .. _translations: @@ -89,13 +83,13 @@ Translations ------------ Community contributors help translate this documentation into several languages. -Note that they have varying degrees of completeness and up-to-dateness. The English -version stands as a reference. +Note that they have varying degrees of completeness and up-to-dateness. +The English version stands as a reference. You can switch between languages by clicking on the flyout menu in the bottom-left corner and selecting the preferred language. -* `Chinese `_ +* `Chinese `_ * `French `_ * `Indonesian `_ * `Japanese `_ @@ -103,12 +97,12 @@ and selecting the preferred language. * `Persian `_ * `Russian `_ * `Spanish `_ -* `Turkish `_ +* `Turkish `_ .. note:: - We set up a GitHub organization and translation workflow to help streamline the - community efforts. Please refer to the translation guide in the `solidity-docs org `_ + We set up a GitHub organization and translation workflow to help streamline the community efforts. + Please refer to the translation guide in the `solidity-docs org `_ for information on how to start a new language or contribute to the community translations. Contents @@ -121,8 +115,8 @@ Contents :caption: Basics introduction-to-smart-contracts.rst - installing-solidity.rst solidity-by-example.rst + installing-solidity.rst .. toctree:: :maxdepth: 2 @@ -161,21 +155,31 @@ Contents .. toctree:: :maxdepth: 2 - :caption: Additional Material + :caption: Advisory content + security-considerations.rst + bugs.rst 050-breaking-changes.rst 060-breaking-changes.rst 070-breaking-changes.rst 080-breaking-changes.rst + +.. toctree:: + :maxdepth: 2 + :caption: Additional Material + natspec-format.rst - security-considerations.rst smtchecker.rst - resources.rst - path-resolution.rst yul.rst + path-resolution.rst + +.. toctree:: + :maxdepth: 2 + :caption: Resources + style-guide.rst common-patterns.rst - bugs.rst + resources.rst contributing.rst - brand-guide.rst language-influences.rst + brand-guide.rst diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst index 9a613d75d3..5473dbb524 100644 --- a/docs/installing-solidity.rst +++ b/docs/installing-solidity.rst @@ -10,12 +10,12 @@ Versioning ========== Solidity versions follow `Semantic Versioning `_. In -addition, patch level releases with major release 0 (i.e. 0.x.y) will not +addition, patch-level releases with major release 0 (i.e. 0.x.y) will not contain breaking changes. That means code that compiles with version 0.x.y can be expected to compile with 0.x.z where z > y. -In addition to releases, we provide **nightly development builds** with the -intention of making it easy for developers to try out upcoming features and +In addition to releases, we provide **nightly development builds** to make +it easy for developers to try out upcoming features and provide early feedback. Note, however, that while the nightly builds are usually very stable, they contain bleeding-edge code from the development branch and are not guaranteed to be always working. Despite our best efforts, they might @@ -33,12 +33,12 @@ Remix `Access Remix online `_, you do not need to install anything. If you want to use it without connection to the Internet, go to -https://github.com/ethereum/remix-live/tree/gh-pages and download the ``.zip`` file as -explained on that page. Remix is also a convenient option for testing nightly builds +https://github.com/ethereum/remix-live/tree/gh-pages#readme and follow the instructions on that page. +Remix is also a convenient option for testing nightly builds without installing multiple Solidity versions. -Further options on this page detail installing commandline Solidity compiler software -on your computer. Choose a commandline compiler if you are working on a larger contract +Further options on this page detail installing command-line Solidity compiler software +on your computer. Choose a command-line compiler if you are working on a larger contract or if you require more compilation options. .. _solcjs: @@ -54,7 +54,7 @@ the full-featured compiler, ``solc``. The usage of ``solcjs`` is documented insi `repository `_. Note: The solc-js project is derived from the C++ -`solc` by using Emscripten which means that both use the same compiler source code. +`solc` by using Emscripten, which means that both use the same compiler source code. `solc-js` can be used in JavaScript projects directly (such as Remix). Please refer to the solc-js repository for instructions. @@ -64,18 +64,18 @@ Please refer to the solc-js repository for instructions. .. note:: - The commandline executable is named ``solcjs``. + The command-line executable is named ``solcjs``. - The commandline options of ``solcjs`` are not compatible with ``solc`` and tools (such as ``geth``) - expecting the behaviour of ``solc`` will not work with ``solcjs``. + The command-line options of ``solcjs`` are not compatible with ``solc`` and tools (such as ``geth``) + expecting the behavior of ``solc`` will not work with ``solcjs``. Docker ====== -Docker images of Solidity builds are available using the ``solc`` image from the ``ethereum`` organisation. -Use the ``stable`` tag for the latest released version, and ``nightly`` for potentially unstable changes in the develop branch. +Docker images of Solidity builds are available using the ``solc`` image from the ``ethereum`` organization. +Use the ``stable`` tag for the latest released version, and ``nightly`` for potentially unstable changes in the ``develop`` branch. -The Docker image runs the compiler executable, so you can pass all compiler arguments to it. +The Docker image runs the compiler executable so that you can pass all compiler arguments to it. For example, the command below pulls the stable version of the ``solc`` image (if you do not have it already), and runs it in a new container, passing the ``--help`` argument. @@ -83,21 +83,27 @@ and runs it in a new container, passing the ``--help`` argument. docker run ethereum/solc:stable --help -You can also specify release build versions in the tag, for example, for the 0.5.4 release. +You can specify release build versions in the tag. For example: .. code-block:: bash - docker run ethereum/solc:0.5.4 --help + docker run ethereum/solc:stable --help + +Note -To use the Docker image to compile Solidity files on the host machine mount a -local folder for input and output, and specify the contract to compile. For example. +Specific compiler versions are supported as the Docker image tag such as `ethereum/solc:0.8.23`. We will be passing the +`stable` tag here instead of specific version tag to ensure that users get the latest version by default and avoid the issue of +an out-of-date version. + +To use the Docker image to compile Solidity files on the host machine, mount a +local folder for input and output, and specify the contract to compile. For example: .. code-block:: bash docker run -v /local/path:/sources ethereum/solc:stable -o /sources/output --abi --bin /sources/Contract.sol You can also use the standard JSON interface (which is recommended when using the compiler with tooling). -When using this interface it is not necessary to mount any directories as long as the JSON input is +When using this interface, it is not necessary to mount any directories as long as the JSON input is self-contained (i.e. it does not refer to any external files that would have to be :ref:`loaded by the import callback `). @@ -130,13 +136,15 @@ The nightly version can be installed using these commands: sudo apt-get install solc Furthermore, some Linux distributions provide their own packages. These packages are not directly -maintained by us, but usually kept up-to-date by the respective package maintainers. +maintained by us but usually kept up-to-date by the respective package maintainers. -For example, Arch Linux has packages for the latest development version: +For example, Arch Linux has packages for the latest development version as AUR packages: `solidity `_ +and `solidity-bin `_. -.. code-block:: bash +.. note:: - pacman -S solidity + Please be aware that `AUR `_ packages + are user-produced content and unofficial packages. Exercise caution when using them. There is also a `snap package `_, however, it is **currently unmaintained**. It is installable in all the `supported Linux distros `_. To @@ -181,7 +189,7 @@ If you need a specific version of Solidity you can install a Homebrew formula directly from Github. View -`solidity.rb commits on Github `_. +`solidity.rb commits on GitHub `_. Copy the commit hash of the version you want and check it out on your machine. @@ -212,23 +220,23 @@ out-of-the-box but it is also meant to be friendly to third-party tools: HTTPS without any authentication, rate limiting or the need to use git. - Content is served with correct `Content-Type` headers and lenient CORS configuration so that it can be directly loaded by tools running in the browser. -- Binaries do not require installation or unpacking (with the exception of older Windows builds +- Binaries do not require installation or unpacking (exception for older Windows builds bundled with necessary DLLs). -- We strive for a high level of backwards-compatibility. Files, once added, are not removed or moved +- We strive for a high level of backward-compatibility. Files, once added, are not removed or moved without providing a symlink/redirect at the old location. They are also never modified in place and should always match the original checksum. The only exception would be broken or - unusable files with a potential to cause more harm than good if left as is. + unusable files with the potential to cause more harm than good if left as is. - Files are served over both HTTP and HTTPS. As long as you obtain the file list in a secure way (via git, HTTPS, IPFS or just have it cached locally) and verify hashes of the binaries after downloading them, you do not have to use HTTPS for the binaries themselves. -The same binaries are in most cases available on the `Solidity release page on Github`_. The -difference is that we do not generally update old releases on the Github release page. This means +The same binaries are in most cases available on the `Solidity release page on GitHub`_. The +difference is that we do not generally update old releases on the GitHub release page. This means that we do not rename them if the naming convention changes and we do not add builds for platforms that were not supported at the time of release. This only happens in ``solc-bin``. The ``solc-bin`` repository contains several top-level directories, each representing a single platform. -Each one contains a ``list.json`` file listing the available binaries. For example in +Each one includes a ``list.json`` file listing the available binaries. For example in ``emscripten-wasm32/list.json`` you will find the following information about version 0.7.4: .. code-block:: json @@ -241,7 +249,6 @@ Each one contains a ``list.json`` file listing the available binaries. For examp "keccak256": "0x300330ecd127756b824aa13e843cb1f43c473cb22eaf3750d5fb9c99279af8c3", "sha256": "0x2b55ed5fec4d9625b6c7b3ab1abd2b7fb7dd2a9c68543bf0323db2c7e2d55af2", "urls": [ - "bzzr://16c5f09109c793db99fe35f037c6092b061bd39260ee7a677c8a97f18c955ab1", "dweb:/ipfs/QmTLs5MuLEWXQkths41HiACoXDiH8zxyqBHGFDRSzVE5CS" ] } @@ -256,10 +263,10 @@ This means that: In this case git is not necessary and symlinks are resolved transparently, either by serving a copy of the file or returning a HTTP redirect. - The file is also available on IPFS at `QmTLs5MuLEWXQkths41HiACoXDiH8zxyqBHGFDRSzVE5CS`_. -- The file might in future be available on Swarm at `16c5f09109c793db99fe35f037c6092b061bd39260ee7a677c8a97f18c955ab1`_. + Please, be aware that the order of items in the ``urls`` array is not predetermined or guaranteed and users should not rely on it. - You can verify the integrity of the binary by comparing its keccak256 hash to ``0x300330ecd127756b824aa13e843cb1f43c473cb22eaf3750d5fb9c99279af8c3``. The hash can be computed - on the command line using ``keccak256sum`` utility provided by `sha3sum`_ or `keccak256() function + on the command-line using ``keccak256sum`` utility provided by `sha3sum`_ or `keccak256() function from ethereumjs-util`_ in JavaScript. - You can also verify the integrity of the binary by comparing its sha256 hash to ``0x2b55ed5fec4d9625b6c7b3ab1abd2b7fb7dd2a9c68543bf0323db2c7e2d55af2``. @@ -297,20 +304,17 @@ This means that: in the long-term. .. _IPFS: https://ipfs.io -.. _Swarm: https://swarm-gateways.net/bzz:/swarm.eth .. _solc-bin: https://github.com/ethereum/solc-bin/ -.. _Solidity release page on github: https://github.com/ethereum/solidity/releases +.. _Solidity release page on GitHub: https://github.com/ethereum/solidity/releases .. _sha3sum: https://github.com/maandree/sha3sum .. _keccak256() function from ethereumjs-util: https://github.com/ethereumjs/ethereumjs-util/blob/master/docs/modules/_hash_.md#const-keccak256 .. _WebAssembly builds: https://emscripten.org/docs/compiling/WebAssembly.html .. _QmTLs5MuLEWXQkths41HiACoXDiH8zxyqBHGFDRSzVE5CS: https://gateway.ipfs.io/ipfs/QmTLs5MuLEWXQkths41HiACoXDiH8zxyqBHGFDRSzVE5CS -.. _16c5f09109c793db99fe35f037c6092b061bd39260ee7a677c8a97f18c955ab1: https://swarm-gateways.net/bzz:/16c5f09109c793db99fe35f037c6092b061bd39260ee7a677c8a97f18c955ab1/ .. _building-from-source: Building from Source ==================== - Prerequisites - All Operating Systems ------------------------------------- @@ -319,19 +323,17 @@ The following are dependencies for all builds of Solidity: +-----------------------------------+-------------------------------------------------------+ | Software | Notes | +===================================+=======================================================+ -| `CMake`_ (version 3.13+) | Cross-platform build file generator. | +| `CMake`_ (version 3.21.3+ on | Cross-platform build file generator. | +| Windows, 3.13+ otherwise) | | +-----------------------------------+-------------------------------------------------------+ | `Boost`_ (version 1.77+ on | C++ libraries. | -| Windows, 1.65+ otherwise) | | +| Windows, 1.67+ otherwise) | | +-----------------------------------+-------------------------------------------------------+ | `Git`_ | Command-line tool for retrieving source code. | +-----------------------------------+-------------------------------------------------------+ -| `z3`_ (version 4.8+, Optional) | For use with SMT checker. | -+-----------------------------------+-------------------------------------------------------+ -| `cvc4`_ (Optional) | For use with SMT checker. | +| `z3`_ (version 4.8.16+, Optional) | For use with SMT checker. | +-----------------------------------+-------------------------------------------------------+ -.. _cvc4: https://cvc4.cs.stanford.edu/web/ .. _Git: https://git-scm.com/download .. _Boost: https://www.boost.org .. _CMake: https://cmake.org/download/ @@ -340,7 +342,7 @@ The following are dependencies for all builds of Solidity: .. note:: Solidity versions prior to 0.5.10 can fail to correctly link against Boost versions 1.70+. A possible workaround is to temporarily rename ``/lib/cmake/Boost-1.70.0`` - prior to running the cmake command to configure solidity. + prior to running the cmake command to configure Solidity. Starting from 0.5.10 linking against Boost 1.70+ should work without manual intervention. @@ -378,7 +380,7 @@ Prerequisites - macOS --------------------- For macOS builds, ensure that you have the latest version of -`Xcode installed `_. +`Xcode installed `_. This contains the `Clang C++ compiler `_, the `Xcode IDE `_ and other Apple development tools that are required for building C++ applications on OS X. @@ -428,7 +430,7 @@ in Visual Studio 2019 Build Tools or Visual Studio 2019: * C++/CLI support .. _Visual Studio 2019: https://www.visualstudio.com/vs/ -.. _Visual Studio 2019 Build Tools: https://www.visualstudio.com/downloads/#build-tools-for-visual-studio-2019 +.. _Visual Studio 2019 Build Tools: https://visualstudio.microsoft.com/vs/older-downloads/#visual-studio-2019-and-other-products We have a helper script which you can use to install all required external dependencies: @@ -448,7 +450,7 @@ To clone the source code, execute the following command: git clone --recursive https://github.com/ethereum/solidity.git cd solidity -If you want to help developing Solidity, +If you want to help develop Solidity, you should fork Solidity and add your personal fork as a second remote: .. code-block:: bash @@ -456,14 +458,14 @@ you should fork Solidity and add your personal fork as a second remote: git remote add personal git@github.com:[username]/solidity.git .. note:: - This method will result in a prerelease build leading to e.g. a flag + This method will result in a pre-release build leading to e.g. a flag being set in each bytecode produced by such a compiler. If you want to re-build a released Solidity compiler, then - please use the source tarball on the github release page: + please use the source tarball on the GitHub release page: https://github.com/ethereum/solidity/releases/download/v0.X.Y/solidity_0.X.Y.tar.gz - (not the "Source code" provided by github). + (not the "Source code" provided by GitHub). Command-Line Build ------------------ @@ -525,24 +527,23 @@ If you are interested what CMake options are available run ``cmake .. -LH``. SMT Solvers ----------- -Solidity can be built against SMT solvers and will do so by default if -they are found in the system. Each solver can be disabled by a `cmake` option. +Solidity can be built against Z3 SMT solver and will do so by default if +it is found in the system. Z3 can be disabled by a ``cmake`` option. *Note: In some cases, this can also be a potential workaround for build failures.* -Inside the build folder you can disable them, since they are enabled by default: +Inside the build folder you can disable Z3, since it is enabled by default: .. code-block:: bash - # disables only Z3 SMT Solver. + # disables Z3 SMT Solver. cmake .. -DUSE_Z3=OFF - # disables only CVC4 SMT Solver. - cmake .. -DUSE_CVC4=OFF +.. note:: - # disables both Z3 and CVC4 - cmake .. -DUSE_CVC4=OFF -DUSE_Z3=OFF + Solidity can optionally use other solvers, namely ``cvc5`` and ``Eldarica``, + but their presence is checked only at runtime, they are not needed for the build to succeed. The Version String in Detail ============================ @@ -579,4 +580,4 @@ Example: 4. A breaking change is introduced --> version is bumped to 0.5.0. 5. The 0.5.0 release is made. -This behaviour works well with the :ref:`version pragma `. +This behavior works well with the :ref:`version pragma `. diff --git a/docs/internals/layout_in_storage.rst b/docs/internals/layout_in_storage.rst index 53670eeb85..557a0a0e20 100644 --- a/docs/internals/layout_in_storage.rst +++ b/docs/internals/layout_in_storage.rst @@ -1,11 +1,17 @@ -.. index:: storage, state variable, mapping +.. index:: storage, state variable, mapping, transient storage -************************************ -Layout of State Variables in Storage -************************************ +********************************************************** +Layout of State Variables in Storage and Transient Storage +********************************************************** .. _storage-inplace-encoding: +.. note:: + The rules described in this section apply for both storage and transient storage data locations. + The layouts are completely independent and don't interfere with each other's variable locations. + Thus storage and transient storage state variables can be safely interleaved without any side effects. + Only value types are supported for transient storage. + State variables of contracts are stored in storage in a compact way such that multiple values sometimes use the same storage slot. Except for dynamically-sized arrays and mappings (see below), data is stored @@ -147,7 +153,7 @@ JSON Output .. _storage-layout-top-level: -The storage layout of a contract can be requested via +The storage (or transient storage) layout of a contract can be requested via the :ref:`standard JSON interface `. The output is a JSON object containing two keys, ``storage`` and ``types``. The ``storage`` object is an array where each element has the following form: @@ -208,18 +214,18 @@ of types), arrays have its ``base`` type, and structs list their ``members`` in the same format as the top-level ``storage`` (see :ref:`above `). -.. note :: +.. note:: The JSON output format of a contract's storage layout is still considered experimental and is subject to change in non-breaking releases of Solidity. -The following example shows a contract and its storage layout, containing -value and reference types, types that are encoded packed, and nested types. +The following example shows a contract and both its storage and transient storage layout, +containing value and reference types, types that are encoded packed, and nested types. .. code-block:: solidity // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.0 <0.9.0; + pragma solidity ^0.8.28; contract A { struct S { uint128 a; @@ -229,15 +235,22 @@ value and reference types, types that are encoded packed, and nested types. } uint x; - uint y; + uint transient y; + uint w; + uint transient z; + S s; address addr; - mapping (uint => mapping (address => bool)) map; + address transient taddr; + mapping(uint => mapping(address => bool)) map; uint[] array; string s1; bytes b1; } +Storage Layout +-------------- + .. code-block:: json { @@ -251,15 +264,15 @@ value and reference types, types that are encoded packed, and nested types. "type": "t_uint256" }, { - "astId": 17, + "astId": 19, "contract": "fileA:A", - "label": "y", + "label": "w", "offset": 0, "slot": "1", "type": "t_uint256" }, { - "astId": 20, + "astId": 24, "contract": "fileA:A", "label": "s", "offset": 0, @@ -267,7 +280,7 @@ value and reference types, types that are encoded packed, and nested types. "type": "t_struct(S)13_storage" }, { - "astId": 22, + "astId": 26, "contract": "fileA:A", "label": "addr", "offset": 0, @@ -275,7 +288,7 @@ value and reference types, types that are encoded packed, and nested types. "type": "t_address" }, { - "astId": 28, + "astId": 34, "contract": "fileA:A", "label": "map", "offset": 0, @@ -283,7 +296,7 @@ value and reference types, types that are encoded packed, and nested types. "type": "t_mapping(t_uint256,t_mapping(t_address,t_bool))" }, { - "astId": 31, + "astId": 37, "contract": "fileA:A", "label": "array", "offset": 0, @@ -291,7 +304,7 @@ value and reference types, types that are encoded packed, and nested types. "type": "t_array(t_uint256)dyn_storage" }, { - "astId": 33, + "astId": 39, "contract": "fileA:A", "label": "s1", "offset": 0, @@ -299,7 +312,7 @@ value and reference types, types that are encoded packed, and nested types. "type": "t_string_storage" }, { - "astId": 35, + "astId": 41, "contract": "fileA:A", "label": "b1", "offset": 0, @@ -405,3 +418,49 @@ value and reference types, types that are encoded packed, and nested types. } } } + +Transient Storage Layout +------------------------ + +.. code-block:: json + + { + "storage": [ + { + "astId": 17, + "contract": "fileA:A", + "label": "y", + "offset": 0, + "slot": "0", + "type": "t_uint256" + }, + { + "astId": 21, + "contract": "fileA:A", + "label": "z", + "offset": 0, + "slot": "1", + "type": "t_uint256" + }, + { + "astId": 28, + "contract": "fileA:A", + "label": "taddr", + "offset": 0, + "slot": "2", + "type": "t_address" + } + ], + "types": { + "t_address": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "t_uint256": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + } \ No newline at end of file diff --git a/docs/internals/optimizer.rst b/docs/internals/optimizer.rst index 11f55e0e3a..604560554a 100644 --- a/docs/internals/optimizer.rst +++ b/docs/internals/optimizer.rst @@ -5,8 +5,11 @@ The Optimizer ************* -The Solidity compiler uses two different optimizer modules: The "old" optimizer -that operates at the opcode level and the "new" optimizer that operates on Yul IR code. +The Solidity compiler involves optimizations at three different levels (in order of execution): + +- Optimizations during code generation based on a direct analysis of Solidity code. +- Optimizing transformations on the Yul IR code. +- Optimizations at the opcode level. The opcode-based optimizer applies a set of `simplification rules `_ to opcodes. It also combines equal code sets and removes unused code. @@ -20,6 +23,17 @@ the function calls. Similarly, if a function is side-effect free and its result is multiplied by zero, you can remove the function call completely. +The codegen-based optimizer affects the initial low-level code produced from the Solidity input. +In the legacy pipeline, the bytecode is generated immediately and most of the optimizations of this +kind are implicit and not configurable, the only exception being an optimization which changes the +order of literals in binary operations. +The IR-based pipeline takes a different approach and produces Yul IR closely matching the structure +of the Solidity code, with nearly all optimizations deferred to the Yul optimizer module. +In that case codegen-level optimization is done only in very limited cases which are difficult to +handle in Yul IR, but are straightforward with the high-level information from analysis phase at hand. +An example of such an optimization is the bypass of checked arithmetic when incrementing the counter +in certain idiomatic ``for`` loops. + Currently, the parameter ``--optimize`` activates the opcode-based optimizer for the generated bytecode and the Yul optimizer for the Yul code generated internally, for example for ABI coder v2. One can use ``solc --ir-optimized --optimize`` to produce an @@ -27,9 +41,15 @@ optimized Yul IR for a Solidity source. Similarly, one can use ``solc --strict-a for a stand-alone Yul mode. .. note:: - The `peephole optimizer `_ and the inliner are always + Some optimizer steps, such as, for example, the `peephole optimizer `_ + and the :ref:`unchecked loop increment optimizer ` are always enabled by default and can only be turned off via the :ref:`Standard JSON `. +.. note:: + An empty optimizer sequence, i.e ``:``, is accepted even without ``--optimize`` in order to fully disable + the user-supplied portion of the Yul :ref:`optimizer sequence `, as by default, + even when the optimizer is not turned on, the :ref:`unused pruner ` step will be run. + You can find more details on both optimizer modules and their optimization steps below. Benefits of Optimizing Solidity Code @@ -275,11 +295,11 @@ backtracking. All components of the Yul-based optimizer module are explained below. The following transformation steps are the main components: -- SSA Transform -- Common Subexpression Eliminator -- Expression Simplifier -- Redundant Assign Eliminator -- Full Inliner +- SSATransform +- CommonSubexpressionEliminator +- ExpressionSimplifier +- UnusedAssignEliminator +- FullInliner .. _optimizer-steps: @@ -293,7 +313,7 @@ on the individual steps and their sequence below. Abbreviation Full name ============ =============================== ``f`` :ref:`block-flattener` -``l`` :ref:`circular-reference-pruner` +``l`` :ref:`circular-references-pruner` ``c`` :ref:`common-subexpression-eliminator` ``C`` :ref:`conditional-simplifier` ``U`` :ref:`conditional-unsimplifier` @@ -315,12 +335,11 @@ Abbreviation Full name ``T`` :ref:`literal-rematerialiser` ``L`` :ref:`load-resolver` ``M`` :ref:`loop-invariant-code-motion` -``r`` :ref:`redundant-assign-eliminator` -``R`` :ref:`reasoning-based-simplifier` - highly experimental ``m`` :ref:`rematerialiser` -``V`` :ref:`SSA-reverser` -``a`` :ref:`SSA-transform` +``V`` :ref:`ssa-reverser` +``a`` :ref:`ssa-transform` ``t`` :ref:`structural-simplifier` +``r`` :ref:`unused-assign-eliminator` ``p`` :ref:`unused-function-parameter-pruner` ``S`` :ref:`unused-store-eliminator` ``u`` :ref:`unused-pruner` @@ -330,10 +349,7 @@ Abbreviation Full name Some steps depend on properties ensured by ``BlockFlattener``, ``FunctionGrouper``, ``ForLoopInitRewriter``. For this reason the Yul optimizer always applies them before applying any steps supplied by the user. -The ReasoningBasedSimplifier is an optimizer step that is currently not enabled -in the default set of steps. It uses an SMT solver to simplify arithmetic expressions -and boolean conditions. It has not received thorough testing or validation yet and can produce -non-reproducible results, so please use with care! +.. _selecting-optimizations: Selecting Optimizations ----------------------- @@ -343,7 +359,7 @@ You can override this sequence and supply your own using the ``--yul-optimizatio .. code-block:: bash - solc --optimize --ir-optimized --yul-optimizations 'dhfoD[xarrscLMcCTU]uljmul:fDnTOc' + solc --optimize --ir-optimized --yul-optimizations 'dhfoD[xarrscLMcCTU]uljmul:fDnTOcmu' The order of steps is significant and affects the quality of the output. Moreover, applying a step may uncover new optimization opportunities for others that were already applied, @@ -400,7 +416,7 @@ and functions can be optimized in isolation without having to traverse the AST c FunctionGrouper ^^^^^^^^^^^^^^^ -The function grouper has to be applied after the disambiguator and the function hoister. +The function grouper has to be applied after the Disambiguator and the FunctionHoister. Its effect is that all topmost elements that are not function definitions are moved into a single block which is the first statement of the root block. @@ -413,14 +429,14 @@ After this step, a program has the following normal form: Where ``I`` is a (potentially empty) block that does not contain any function definitions (not even recursively) and ``F`` is a list of function definitions such that no function contains a function definition. -The benefit of this stage is that we always know where the list of function begins. +The benefit of this stage is that we always know where the list of functions begins. .. _for-loop-condition-into-body: ForLoopConditionIntoBody ^^^^^^^^^^^^^^^^^^^^^^^^ -This transformation moves the loop-iteration condition of a for-loop into loop body. +This transformation moves the loop-iteration condition of a ``for`` loop into loop body. We need this transformation because :ref:`expression-splitter` will not apply to iteration condition expressions (the ``C`` in the following example). @@ -439,7 +455,7 @@ is transformed to Body... } -This transformation can also be useful when paired with ``LoopInvariantCodeMotion``, since +This transformation can also be useful when paired with LoopInvariantCodeMotion, since invariants in the loop-invariant conditions can then be taken outside the loop. .. _for-loop-init-rewriter: @@ -447,7 +463,7 @@ invariants in the loop-invariant conditions can then be taken outside the loop. ForLoopInitRewriter ^^^^^^^^^^^^^^^^^^^ -This transformation moves the initialization part of a for-loop to before +This transformation moves the initialization part of a ``for`` loop to before the loop: .. code-block:: text @@ -466,7 +482,7 @@ is transformed to } This eases the rest of the optimization process because we can ignore -the complicated scoping rules of the for loop initialisation block. +the complicated scoping rules of the ``for`` loop initialization block. .. _var-decl-initializer: @@ -575,7 +591,7 @@ It is not applied to loop iteration-condition, because the loop control flow doe this "outlining" of the inner expressions in all cases. We can sidestep this limitation by applying :ref:`for-loop-condition-into-body` to move the iteration condition into loop body. -The final program should be in a form such that (with the exception of loop conditions) +The final program should be in an *expression-split form*, where (with the exception of loop conditions) function calls cannot appear nested inside expressions and all function call arguments have to be variables. @@ -584,7 +600,7 @@ and it is also easier to perform function call inlining. Furthermore, it is simp to replace individual parts of expressions or re-organize the "expression tree". The drawback is that such code is much harder to read for humans. -.. _SSA-transform: +.. _ssa-transform: SSATransform ^^^^^^^^^^^^ @@ -629,25 +645,25 @@ are not modified) perform the following transforms: Furthermore, always record the current value of ``i`` used for ``a`` and replace each reference to ``a`` by ``a_i``. The current value mapping is cleared for a variable ``a`` at the end of each block -in which it was assigned to and at the end of the for loop init block if it is assigned -inside the for loop body or post block. +in which it was assigned to and at the end of the ``for`` loop init block if it is assigned +inside the ``for`` loop body or post block. If a variable's value is cleared according to the rule above and the variable is declared outside the block, a new SSA variable will be created at the location where control flow joins, this includes the beginning of loop post/body block and the location right after -If/Switch/ForLoop/Block statement. +``if``/``switch``/``for``/block statement. -After this stage, the Redundant Assign Eliminator is recommended to remove the unnecessary +After this stage, the UnusedAssignEliminator is recommended to remove the unnecessary intermediate assignments. -This stage provides best results if the Expression Splitter and the Common Subexpression Eliminator +This stage provides best results if the ExpressionSplitter and the CommonSubexpressionEliminator are run right before it, because then it does not generate excessive amounts of variables. -On the other hand, the Common Subexpression Eliminator could be more efficient if run after the +On the other hand, the CommonSubexpressionEliminator could be more efficient if run after the SSA transform. -.. _redundant-assign-eliminator: +.. _unused-assign-eliminator: -RedundantAssignEliminator -^^^^^^^^^^^^^^^^^^^^^^^^^ +UnusedAssignEliminator +^^^^^^^^^^^^^^^^^^^^^^ The SSA transform always generates an assignment of the form ``a := a_i``, even though these might be unnecessary in many cases, like the following example: @@ -675,7 +691,7 @@ The SSA transform converts this snippet to the following: sstore(a_3, 1) } -The Redundant Assign Eliminator removes all the three assignments to ``a``, because +The UnusedAssignEliminator removes all the three assignments to ``a``, because the value of ``a`` is not used and thus turn this snippet into strict SSA form: @@ -688,7 +704,7 @@ snippet into strict SSA form: sstore(a_3, 1) } -Of course the intricate parts of determining whether an assignment is redundant or not +Of course the intricate parts of determining whether an assignment is unused or not are connected to joining control flow. The component works as follows in detail: @@ -700,7 +716,7 @@ mapping from assignment statements to the three states value will be used later by a reference to the variable. When an assignment is visited, it is added to the mapping in the "undecided" state -(see remark about for loops below) and every other assignment to the same variable +(see remark about ``for`` loops below) and every other assignment to the same variable that is still in the "undecided" state is changed to "unused". When a variable is referenced, the state of any assignment to that variable still in the "undecided" state is changed to "used". @@ -715,7 +731,7 @@ Conflicting values are resolved in the following way: - "unused", "used" -> "used" - "undecided", "used" -> "used" -For for-loops, the condition, body and post-part are visited twice, taking +For ``for`` loops, the condition, body and post-part are visited twice, taking the joining control-flow at the condition into account. In other words, we create three control flow paths: Zero runs of the loop, one run and two runs and then combine them at the end. @@ -741,13 +757,13 @@ and thus .. code-block:: none - max(s, f(s), f(f(s))) = max(s, f(s), f(f(s)), f(f(f(s))), ...). + max(s, f(s), f(f(s))) = max(s, f(s), f(f(s)), f(f(f(s))), ...) In summary, running the loop at most twice is enough because there are only three different states. -For switch statements that have a "default"-case, there is no control-flow -part that skips the switch. +For ``switch`` statements that have a default case, there is no control-flow +part that skips the ``switch``. When a variable goes out of scope, all statements still in the "undecided" state are changed to "unused", unless the variable is the return @@ -777,7 +793,7 @@ The following parts make an expression non-movable: DataflowAnalyzer ^^^^^^^^^^^^^^^^ -The Dataflow Analyzer is not an optimizer step itself but is used as a tool +The DataflowAnalyzer is not an optimizer step itself but is used as a tool by other components. While traversing the AST, it tracks the current value of each variable, as long as that value is a movable expression. It records the variables that are part of the expression @@ -788,7 +804,7 @@ of the currently stored expression for ``b``. At control-flow joins, knowledge about variables is cleared if they have or would be assigned in any of the control-flow paths. For instance, upon entering a -for loop, all variables are cleared that will be assigned during the +``for`` loop, all variables are cleared that will be assigned during the body or the post block. Expression-Scale Simplifications @@ -802,7 +818,7 @@ and hopefully simpler expressions. CommonSubexpressionEliminator ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -This step uses the Dataflow Analyzer and replaces subexpressions that +This step uses the DataflowAnalyzer and replaces subexpressions that syntactically match the current value of a variable by a reference to that variable. This is an equivalence transform because such subexpressions have to be movable. @@ -812,24 +828,24 @@ current value if the value is an identifier. The combination of the two rules above allow to compute a local value numbering, which means that if two variables have the same -value, one of them will always be unused. The Unused Pruner or the -Redundant Assign Eliminator will then be able to fully eliminate such +value, one of them will always be unused. The UnusedPruner or the +UnusedAssignEliminator will then be able to fully eliminate such variables. -This step is especially efficient if the expression splitter is run +This step is especially efficient if the ExpressionSplitter is run before. If the code is in pseudo-SSA form, the values of variables are available for a longer time and thus we have a higher chance of expressions to be replaceable. -The expression simplifier will be able to perform better replacements -if the common subexpression eliminator was run right before it. +The ExpressionSimplifier will be able to perform better replacements +if the CommonSubexpressionEliminator was run right before it. .. _expression-simplifier: -Expression Simplifier -^^^^^^^^^^^^^^^^^^^^^ +ExpressionSimplifier +^^^^^^^^^^^^^^^^^^^^ -The Expression Simplifier uses the Dataflow Analyzer and makes use +The ExpressionSimplifier uses the DataflowAnalyzer and makes use of a list of equivalence transforms on expressions like ``X + 0 -> X`` to simplify the code. @@ -841,7 +857,7 @@ even when the code is in pseudo-SSA form. Some of the patterns like ``X - X -> 0`` can only be applied as long as the expression ``X`` is movable, because otherwise it would remove its potential side-effects. Since variable references are always movable, even if their current -value might not be, the Expression Simplifier is again more powerful +value might not be, the ExpressionSimplifier is again more powerful in split or pseudo-SSA form. .. _literal-rematerialiser: @@ -861,28 +877,12 @@ currently stored in storage resp. memory, if known. Works best if the code is in SSA form. -Prerequisite: Disambiguator, ForLoopInitRewriter. - -.. _reasoning-based-simplifier: - -ReasoningBasedSimplifier -^^^^^^^^^^^^^^^^^^^^^^^^ - -This optimizer uses SMT solvers to check whether ``if`` conditions are constant. - -- If ``constraints AND condition`` is UNSAT, the condition is never true and the whole body can be removed. -- If ``constraints AND NOT condition`` is UNSAT, the condition is always true and can be replaced by ``1``. - -The simplifications above can only be applied if the condition is movable. - -It is only effective on the EVM dialect, but safe to use on other dialects. - -Prerequisite: Disambiguator, SSATransform. +Prerequisites: Disambiguator, ForLoopInitRewriter. Statement-Scale Simplifications ------------------------------- -.. _circular-reference-pruner: +.. _circular-references-pruner: CircularReferencesPruner ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -895,7 +895,7 @@ neither externally referenced nor referenced from the outermost context. ConditionalSimplifier ^^^^^^^^^^^^^^^^^^^^^ -The Conditional Simplifier inserts assignments to condition variables if the value can be determined +The ConditionalSimplifier inserts assignments to condition variables if the value can be determined from the control-flow. Destroys SSA form. @@ -906,12 +906,12 @@ we cannot assign a specific value. Current features: -- switch cases: insert " := " -- after if statement with terminating control-flow, insert " := 0" +- ``switch`` cases: insert `` := `` +- after ``if`` statement with terminating control-flow, insert `` := 0`` Future features: -- allow replacements by "1" +- allow replacements by ``1`` - take termination of user-defined functions into account Works best with SSA form and if dead code removal has run before. @@ -923,7 +923,7 @@ Prerequisite: Disambiguator. ConditionalUnsimplifier ^^^^^^^^^^^^^^^^^^^^^^^ -Reverse of Conditional Simplifier. +Reverse of ConditionalSimplifier. .. _control-flow-simplifier: @@ -932,14 +932,14 @@ ControlFlowSimplifier Simplifies several control-flow structures: -- replace if with empty body with pop(condition) -- remove empty default switch case -- remove empty switch case if no default case exists -- replace switch with no cases with pop(expression) -- turn switch with single case into if -- replace switch with only default case with pop(expression) and body -- replace switch with const expr with matching case body -- replace ``for`` with terminating control flow and without other break/continue by ``if`` +- replace ``if`` with empty body with ``pop(condition)`` +- remove empty default ``switch`` case +- remove empty ``switch`` case if no default case exists +- replace ``switch`` with no cases with ``pop(expression)`` +- turn ``switch`` with single case into ``if`` +- replace ``switch`` with only default case with ``pop(expression)`` and body +- replace ``switch`` with const expr with matching case body +- replace ``for`` with terminating control flow and without other ``break``/``continue`` by ``if`` - remove ``leave`` at the end of a function. None of these operations depend on the data flow. The StructuralSimplifier @@ -949,6 +949,7 @@ The ControlFlowSimplifier does record the presence or absence of ``break`` and ``continue`` statements during its traversal. Prerequisite: Disambiguator, FunctionHoister, ForLoopInitRewriter. + Important: Introduces EVM opcodes and thus can only be used on EVM code for now. .. _dead-code-eliminator: @@ -959,15 +960,16 @@ DeadCodeEliminator This optimization stage removes unreachable code. Unreachable code is any code within a block which is preceded by a -leave, return, invalid, break, continue, selfdestruct, revert or by a call to a user-defined function that recurses infinitely. +``leave``, ``return``, ``invalid``, ``break``, ``continue``, ``selfdestruct``, ``revert`` or by +a call to a user-defined function that recurses infinitely. Function definitions are retained as they might be called by earlier code and thus are considered reachable. -Because variables declared in a for loop's init block have their scope extended to the loop body, +Because variables declared in a ``for`` loop's init block have their scope extended to the loop body, we require ForLoopInitRewriter to run before this step. -Prerequisite: ForLoopInitRewriter, Function Hoister, Function Grouper +Prerequisites: ForLoopInitRewriter, FunctionHoister, FunctionGrouper. .. _equal-store-eliminator: @@ -978,12 +980,12 @@ This steps removes ``mstore(k, v)`` and ``sstore(k, v)`` calls if there was a previous call to ``mstore(k, v)`` / ``sstore(k, v)``, no other store in between and the values of ``k`` and ``v`` did not change. -This simple step is effective if run after the SSA transform and the -Common Subexpression Eliminator, because SSA will make sure that the variables -will not change and the Common Subexpression Eliminator re-uses exactly the same +This simple step is effective if run after the SSATransform and the +CommonSubexpressionEliminator, because SSA will make sure that the variables +will not change and the CommonSubexpressionEliminator re-uses exactly the same variable if the value is known to be the same. -Prerequisites: Disambiguator, ForLoopInitRewriter +Prerequisites: Disambiguator, ForLoopInitRewriter. .. _unused-pruner: @@ -992,8 +994,8 @@ UnusedPruner This step removes the definitions of all functions that are never referenced. -It also removes the declaration of variables that are never referenced. -If the declaration assigns a value that is not movable, the expression is retained, +It also removes declarations of variables that are never referenced. +If a declaration assigns a value that is not movable, the expression is retained, but its value is discarded. All movable expression statements (expressions that are not assigned) are removed. @@ -1006,22 +1008,22 @@ StructuralSimplifier This is a general step that performs various kinds of simplifications on a structural level: -- replace if statement with empty body by ``pop(condition)`` -- replace if statement with true condition by its body -- remove if statement with false condition -- turn switch with single case into if -- replace switch with only default case by ``pop(expression)`` and body -- replace switch with literal expression by matching case body -- replace for loop with false condition by its initialization part +- replace ``if`` statement with empty body by ``pop(condition)`` +- replace ``if`` statement with true condition by its body +- remove ``if`` statement with false condition +- turn ``switch`` with single case into ``if`` +- replace ``switch`` with only default case by ``pop(expression)`` and body +- replace ``switch`` with literal expression by matching case body +- replace ``for`` loop with false condition by its initialization part -This component uses the Dataflow Analyzer. +This component uses the DataflowAnalyzer. .. _block-flattener: BlockFlattener ^^^^^^^^^^^^^^ -This stage eliminates nested blocks by inserting the statement in the +This stage eliminates nested blocks by inserting the statements in the inner block at the appropriate place in the outer block. It depends on the FunctionGrouper and does not flatten the outermost block to keep the form produced by the FunctionGrouper. @@ -1062,10 +1064,9 @@ This optimization moves movable SSA variable declarations outside the loop. Only statements at the top level in a loop's body or post block are considered, i.e variable declarations inside conditional branches will not be moved out of the loop. -Requirements: +ExpressionSplitter and SSATransform should be run upfront to obtain better results. -- The Disambiguator, ForLoopInitRewriter and FunctionHoister must be run upfront. -- Expression splitter and SSA transform should be run upfront to obtain better result. +Prerequisites: Disambiguator, ForLoopInitRewriter, FunctionHoister. Function-Level Optimizations @@ -1092,7 +1093,7 @@ function ``f_1`` that takes only one argument, i.e., Other optimization steps will be able to make more simplifications to the function. The optimization step is mainly useful for functions that would not be inlined. -Prerequisites: Disambiguator, FunctionHoister +Prerequisites: Disambiguator, FunctionHoister. LiteralRematerialiser is recommended as a prerequisite, even though it's not required for correctness. @@ -1122,7 +1123,7 @@ The step LiteralRematerialiser is not required for correctness. It helps deal wi ``function f(x) -> y { revert(y, y} }`` where the literal ``y`` will be replaced by its value ``0``, allowing us to rewrite the function. -.. index:: ! unused store eliminator +.. index:: ! UnusedStoreEliminator .. _unused-store-eliminator: UnusedStoreEliminator @@ -1149,7 +1150,7 @@ For example, the following code sstore(c, 3) } -will be transformed into the code below after the Unused Store Eliminator step is run +will be transformed into the code below after the UnusedStoreEliminator step is run .. code-block:: yul @@ -1159,10 +1160,10 @@ will be transformed into the code below after the Unused Store Eliminator step i sstore(c, 3) } -For memory store operations, things are generally simpler, at least in the outermost yul block as all such +For memory store operations, things are generally simpler, at least in the outermost Yul block as all such statements will be removed if they are never read from in any code path. At function analysis level, however, the approach is similar to ``sstore``, as we do not know whether the memory location will -be read once we leave the function's scope, so the statement will be removed only if all code code paths lead to a memory overwrite. +be read once we leave the function's scope, so the statement will be removed only if all code paths lead to a memory overwrite. Best run in SSA form. @@ -1177,8 +1178,8 @@ If two functions are syntactically equivalent, while allowing variable renaming but not any re-ordering, then any reference to one of the functions is replaced by the other. -The actual removal of the function is performed by the Unused Pruner. +The actual removal of the function is performed by the UnusedPruner. Function Inlining ----------------- @@ -1199,7 +1200,7 @@ Furthermore, for all parameters, all of the following need to be true: - The argument is movable. - The parameter is either referenced less than twice in the function body, or the argument is rather cheap - ("cost" of at most 1, like a constant up to 0xff). + ("cost" of at most 1, like a constant up to ``0xff``). Example: The function to be inlined has the form of ``function f(...) -> r { r := E }`` where ``E`` is an expression that does not reference ``r`` and all arguments in the function call are movable expressions. @@ -1213,11 +1214,11 @@ This component can only be used on sources with unique names. FullInliner ^^^^^^^^^^^ -The Full Inliner replaces certain calls of certain functions +The FullInliner replaces certain calls of certain functions by the function's body. This is not very helpful in most cases, because it just increases the code size but does not have a benefit. Furthermore, code is usually very expensive and we would often rather have shorter -code than more efficient code. In same cases, though, inlining a function +code than more efficient code. In some cases, though, inlining a function can have positive effects on subsequent optimizer steps. This is the case if one of the function arguments is a constant, for example. @@ -1237,6 +1238,11 @@ we can run the optimizer on this specialized function. If it results in heavy gains, the specialized function is kept, otherwise the original function is used instead. +FunctionHoister and ExpressionSplitter are recommended as prerequisites since they make the step +more efficient, but are not required for correctness. +In particular, function calls with other function calls as arguments are not inlined, but running +ExpressionSplitter beforehand ensures that there are no such calls in the input. + Cleanup ------- @@ -1250,7 +1256,7 @@ variables as much as possible. ExpressionJoiner ^^^^^^^^^^^^^^^^ -This is the opposite operation of the expression splitter. It turns a sequence of +This is the opposite operation of the ExpressionSplitter. It turns a sequence of variable declarations that have exactly one reference into a complex expression. This stage fully preserves the order of function calls and opcode executions. It does not make use of any information concerning the commutativity of the opcodes; @@ -1270,21 +1276,21 @@ Because of that, the snippet ``let x := add(0, 2) let y := mul(x, 3)`` is transformed to ``let y := mul(add(0, 2), 3)``, even though the ``add`` opcode would be executed after the evaluation of the literal ``3``. -.. _SSA-reverser: +.. _ssa-reverser: SSAReverser ^^^^^^^^^^^ -This is a tiny step that helps in reversing the effects of the SSA transform -if it is combined with the Common Subexpression Eliminator and the -Unused Pruner. +This is a tiny step that helps in reversing the effects of the SSATransform +if it is combined with the CommonSubexpressionEliminator and the +UnusedPruner. -The SSA form we generate is detrimental to code generation on the EVM and -WebAssembly alike because it generates many local variables. It would +The SSA form we generate is detrimental to code generation +because it produces many local variables. It would be better to just re-use existing variables with assignments instead of fresh variable declarations. -The SSA transform rewrites +The SSATransform rewrites .. code-block:: yul @@ -1302,7 +1308,7 @@ to a := a_2 The problem is that instead of ``a``, the variable ``a_1`` is used -whenever ``a`` was referenced. The SSA transform changes statements +whenever ``a`` was referenced. The SSATransform changes statements of this form by just swapping out the declaration and the assignment. The above snippet is turned into @@ -1315,10 +1321,10 @@ snippet is turned into let a_2 := a This is a very simple equivalence transform, but when we now run the -Common Subexpression Eliminator, it will replace all occurrences of ``a_1`` -by ``a`` (until ``a`` is re-assigned). The Unused Pruner will then +CommonSubexpressionEliminator, it will replace all occurrences of ``a_1`` +by ``a`` (until ``a`` is re-assigned). The UnusedPruner will then eliminate the variable ``a_1`` altogether and thus fully reverse the -SSA transform. +SSATransform. .. _stack-compressor: @@ -1349,9 +1355,9 @@ is comparatively cheap to evaluate. Furthermore, it is only semantically equival the value of the expression did not change between the point of assignment and the point of use. The main benefit of this stage is that it can save stack slots if it leads to a variable being eliminated completely (see below), but it can also -save a DUP opcode on the EVM if the expression is very cheap. +save a ``DUP`` opcode on the EVM if the expression is very cheap. -The Rematerialiser uses the Dataflow Analyzer to track the current values of variables, +The Rematerialiser uses the DataflowAnalyzer to track the current values of variables, which are always movable. If the value is very cheap or the variable was explicitly requested to be eliminated, the variable reference is replaced by its current value. @@ -1399,14 +1405,62 @@ into The LiteralRematerialiser should be run before this step. +Codegen-Based Optimizer Module +============================== -WebAssembly specific --------------------- +Currently, the codegen-based optimizer module provides two optimizations. -MainFunction -^^^^^^^^^^^^ +The first one, available in the legacy code generator, moves literals to the right side of +commutative binary operators, which helps exploit their associativity. + +The other one, available in the IR-based code generator, enables the use of unchecked arithmetic +when generating code for incrementing the counter variable of certain idiomatic ``for`` loops. +This avoids wasting gas by identifying some conditions that guarantee that the counter variable +cannot overflow. +This eliminates the need to use a verbose unchecked arithmetic block inside the loop body to +increment the counter variable. + +.. _unchecked-loop-optimizer: + +Unchecked Loop Increment +------------------------ + +Introduced in Solidity ``0.8.22``, the overflow check optimization step is concerned with identifying +the conditions under which the ``for`` loop counter can be safely incremented +without overflow checks. + +This optimization is **only** applied to ``for`` loops of the general form: + +.. code-block:: solidity + + for (uint i = X; i < Y; ++i) { + // variable i is not modified in the loop body + } + +The condition and the fact that the counter variable is only ever incremented +guarantee that it never overflows. +The precise requirements for the loop to be eligible for the optimization are as follows: + +- The loop condition is a comparison of the form ``i < Y``, for a local counter variable ``i`` + (called the "loop counter" hereon) and an expression ``Y``. +- The built-in operator ``<`` is necessarily used in the loop condition and is the only operator + that triggers the optimization. ``<=`` and the like are intentionally excluded. Additionally, + user-defined operators are **not** eligible. +- The loop expression is a prefix or postfix increment of the counter variable, i.e, ``i++`` or ``++i``. +- The loop counter is a local variable of a built-in integer type. +- The loop counter is **not** modified by the loop body or by the expression used as the loop condition. +- The comparison is performed on the same type as the loop counter, meaning that the type of the + right-hand-side expression is implicitly convertible to the type of the counter, such that the latter + is not implicitly widened before the comparison. -Changes the topmost block to be a function with a specific name ("main") which has no -inputs nor outputs. +To clarify the last condition, consider the following example: + +.. code-block:: solidity + + for (uint8 i = 0; i < uint16(1000); i++) { + // ... + } -Depends on the Function Grouper. +In this case, the counter ``i`` has its type implicitly converted from ``uint8`` +to ``uint16`` before the comparison and the condition is in fact never false, so +the overflow check for the increment cannot be removed. diff --git a/docs/internals/source_mappings.rst b/docs/internals/source_mappings.rst index 11027b8aca..68c29d955e 100644 --- a/docs/internals/source_mappings.rst +++ b/docs/internals/source_mappings.rst @@ -26,7 +26,7 @@ that are not part of the original input but are referenced from the source mappings. These source files together with their identifiers can be obtained via ``output['contracts'][sourceName][contractName]['evm']['bytecode']['generatedSources']``. -.. note :: +.. note:: In the case of instructions that are not associated with any particular source file, the source mapping assigns an integer identifier of ``-1``. This may happen for bytecode sections stemming from compiler-generated inline assembly statements. diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst index 875e5ebc86..45857c3f89 100644 --- a/docs/introduction-to-smart-contracts.rst +++ b/docs/introduction-to-smart-contracts.rst @@ -85,13 +85,14 @@ registering with a username and password, all you need is an Ethereum keypair. .. code-block:: solidity // SPDX-License-Identifier: GPL-3.0 - pragma solidity ^0.8.4; + pragma solidity ^0.8.26; + // This will only compile via IR contract Coin { // The keyword "public" makes variables // accessible from other contracts address public minter; - mapping (address => uint) public balances; + mapping(address => uint) public balances; // Events allow clients to react to specific // contract changes you declare @@ -118,12 +119,7 @@ registering with a username and password, all you need is an Ethereum keypair. // Sends an amount of existing coins // from any caller to an address function send(address receiver, uint amount) public { - if (amount > balances[msg.sender]) - revert InsufficientBalance({ - requested: amount, - available: balances[msg.sender] - }); - + require(amount <= balances[msg.sender], InsufficientBalance(amount, balances[msg.sender])); balances[msg.sender] -= amount; balances[receiver] += amount; emit Sent(msg.sender, receiver, amount); @@ -151,12 +147,12 @@ You do not need to do this, the compiler figures it out for you. .. index:: mapping -The next line, ``mapping (address => uint) public balances;`` also +The next line, ``mapping(address => uint) public balances;`` also creates a public state variable, but it is a more complex datatype. The :ref:`mapping ` type maps addresses to :ref:`unsigned integers `. Mappings can be seen as `hash tables `_ which are -virtually initialised such that every possible key exists from the start and is mapped to a +virtually initialized such that every possible key exists from the start and is mapped to a value whose byte-representation is all zeros. However, it is neither possible to obtain a list of all keys of a mapping, nor a list of all values. Record what you added to the mapping, or use it in a context where this is not needed. Or @@ -185,7 +181,7 @@ arguments ``from``, ``to`` and ``amount``, which makes it possible to track transactions. To listen for this event, you could use the following -JavaScript code, which uses `web3.js `_ to create the ``Coin`` contract object, +JavaScript code, which uses `web3.js `_ to create the ``Coin`` contract object, and any user interface calls the automatically generated ``balances`` function from above: .. code-block:: javascript @@ -225,8 +221,8 @@ than the maximum value of ``uint`` (``2**256 - 1``). This is also true for the s :ref:`Errors ` allow you to provide more information to the caller about why a condition or operation failed. Errors are used together with the :ref:`revert statement `. The ``revert`` statement unconditionally -aborts and reverts all changes similar to the ``require`` function, but it also -allows you to provide the name of an error and additional data which will be supplied to the caller +aborts and reverts all changes, much like the :ref:`require function `. +Both approaches allow you to provide the name of an error and additional data which will be supplied to the caller (and eventually to the front-end application or block explorer) so that a failure can more easily be debugged or reacted upon. @@ -282,7 +278,7 @@ the source account is also not modified. Furthermore, a transaction is always cryptographically signed by the sender (creator). This makes it straightforward to guard access to specific modifications of the database. In the example of the electronic currency, a simple check ensures that -only the person holding the keys to the account can transfer money from it. +only the person holding the keys to the account can transfer some compensation, e.g. Ether, from it. .. index:: ! block @@ -300,11 +296,11 @@ and then they will be executed and distributed among all participating nodes. If two transactions contradict each other, the one that ends up being second will be rejected and not become part of the block. -These blocks form a linear sequence in time and that is where the word "blockchain" -derives from. Blocks are added to the chain in rather regular intervals - for -Ethereum this is roughly every 17 seconds. +These blocks form a linear sequence in time, and that is where the word "blockchain" derives from. +Blocks are added to the chain at regular intervals, although these intervals may be subject to change in the future. +For the most up-to-date information, it is recommended to monitor the network, for example, on `Etherscan `_. -As part of the "order selection mechanism" (which is called "mining") it may happen that +As part of the "order selection mechanism", which is called `attestation `_, it may happen that blocks are reverted from time to time, but only at the "tip" of the chain. The more blocks are added on top of a particular block, the less likely this block will be reverted. So it might be that your transactions are reverted and even removed from the blockchain, but the longer you wait, the less @@ -419,13 +415,15 @@ In case of an exception that reverts changes, already used up gas is not refunde Since EVM executors can choose to include a transaction or not, transaction senders cannot abuse the system by setting a low gas price. -.. index:: ! storage, ! memory, ! stack +.. index:: ! storage, ! memory, ! stack, ! transient storage -Storage, Memory and the Stack -============================= +.. _locations: + +Storage, Transient Storage, Memory and the Stack +================================================ -The Ethereum Virtual Machine has three areas where it can store data: -storage, memory and the stack. +The Ethereum Virtual Machine has different areas where it can store data with the most +prominent being storage, transient storage, memory and the stack. Each account has a data area called **storage**, which is persistent between function calls and transactions. @@ -436,7 +434,15 @@ you should minimize what you store in persistent storage to what the contract ne Store data like derived calculations, caching, and aggregates outside of the contract. A contract can neither read nor write to any storage apart from its own. -The second data area is called **memory**, of which a contract obtains +Similar to storage, there is another data area called **transient storage**, +where the main difference is that it is reset at the end of each transaction. +The values stored in this data location persist only across function calls originating +from the first call of the transaction. +When the transaction ends, the transient storage is reset and the values stored there +become unavailable to calls in subsequent transactions. +Despite this, the cost of reading and writing to transient storage is significantly lower than for storage. + +The third data area is called **memory**, of which a contract obtains a freshly cleared instance for each message call. Memory is linear and can be addressed at byte level, but reads are limited to a width of 256 bits, while writes can be either 8 bits or 256 bits wide. Memory is expanded by a word (256-bit), when @@ -458,6 +464,31 @@ in order to get deeper access to the stack, but it is not possible to just access arbitrary elements deeper in the stack without first removing the top of the stack. +Calldata, Returndata and Code +============================= + +There are also other data areas which are not as apparent as those discussed previously. +However, they are routinely used during the execution of smart contract transactions. + +The calldata region is the data sent to a transaction as part of a smart contract transaction. +For example, when creating a contract, calldata would be the constructor code of the new contract. +The parameters of external functions are always initially stored in calldata in an ABI-encoded form +and only then decoded into the location specified in their declaration. +If declared as ``memory``, the compiler will eagerly decode them into memory at the beginning of the function, +while marking them as ``calldata`` means that this will be done lazily, only when accessed. +Value types and ``storage`` pointers are decoded directly onto the stack. + +The returndata is the way a smart contract can return a value after a call. +In general, external Solidity functions use the ``return`` keyword to ABI-encode values into the returndata area. + +The code is the region where the EVM instructions of a smart contract are stored. +Code is the bytes read, interpreted, and executed by the EVM during smart contract execution. +Instruction data stored in the code is persistent as part of a contract account state field. +Immutable and constant variables are stored in the code region. +All references to immutables are replaced with the values assigned to them. +A similar process is performed for constants which have their expressions inlined +in the places where they are referenced in the smart contract code. + .. index:: ! instruction Instruction Set @@ -562,6 +593,25 @@ is removed from the state. Removing the contract in theory sounds like a good idea, but it is potentially dangerous, as if someone sends Ether to removed contracts, the Ether is forever lost. +.. warning:: + From ``EVM >= Cancun`` onwards, ``selfdestruct`` will **only** send all Ether in the account to the given recipient and not destroy the contract. + However, when ``selfdestruct`` is called in the same transaction that creates the contract calling it, + the behaviour of ``selfdestruct`` before Cancun hardfork (i.e., ``EVM <= Shanghai``) is preserved and will destroy the current contract, + deleting any data, including storage keys, code and the account itself. + See `EIP-6780 `_ for more details. + + The new behaviour is the result of a network-wide change that affects all contracts present on + the Ethereum mainnet and testnets. + It is important to note that this change is dependent on the EVM version of the chain on which + the contract is deployed. + The ``--evm-version`` setting used when compiling the contract has no bearing on it. + + Also, note that the ``selfdestruct`` opcode has been deprecated in Solidity version 0.8.18, + as recommended by `EIP-6049 `_. + The deprecation is still in effect and the compiler will still emit warnings on its use. + Any use in newly deployed contracts is strongly discouraged even if the new behavior is taken into account. + Future changes to the EVM might further reduce the functionality of the opcode. + .. warning:: Even if a contract is removed by ``selfdestruct``, it is still part of the history of the blockchain and probably retained by most Ethereum nodes. @@ -584,9 +634,9 @@ Precompiled Contracts ===================== There is a small set of contract addresses that are special: -The address range between ``1`` and (including) ``8`` contains +The address range between ``1`` and (including) ``0x0a`` contains "precompiled contracts" that can be called as any other contract -but their behaviour (and their gas consumption) is not defined +but their behavior (and their gas consumption) is not defined by EVM code stored at that address (they do not contain code) but instead is implemented in the EVM execution environment itself. diff --git a/docs/ir-breaking-changes.rst b/docs/ir-breaking-changes.rst index 7e4efce688..4917cfc715 100644 --- a/docs/ir-breaking-changes.rst +++ b/docs/ir-breaking-changes.rst @@ -15,13 +15,13 @@ The IR-based code generator was introduced with an aim to not only allow code generation to be more transparent and auditable but also to enable more powerful optimization passes that span across functions. -You can enable it on the command line using ``--via-ir`` +You can enable it on the command-line using ``--via-ir`` or with the option ``{"viaIR": true}`` in standard-json and we encourage everyone to try it out! For several reasons, there are tiny semantic differences between the old and the IR-based code generator, mostly in areas where we would not -expect people to rely on this behaviour anyway. +expect people to rely on this behavior anyway. This section highlights the main differences between the old and the IR-based codegen. Semantic Only Changes @@ -30,6 +30,8 @@ Semantic Only Changes This section lists the changes that are semantic-only, thus potentially hiding new and different behavior in existing code. +.. _state-variable-initialization-order: + - The order of state variable initialization has changed in case of inheritance. The order used to be: @@ -122,8 +124,8 @@ hiding new and different behavior in existing code. modifier mod() { _; _; } } - If you execute ``f(0)`` in the old code generator, it will return ``2``, while - it will return ``1`` when using the new code generator. + If you execute ``f(0)`` in the old code generator, it will return ``1``, while + it will return ``0`` when using the new code generator. .. code-block:: solidity @@ -174,8 +176,8 @@ hiding new and different behavior in existing code. The function ``preincr_u8(1)`` returns the following values: - - Old code generator: 3 (``1 + 2``) but the return value is unspecified in general - - New code generator: 4 (``2 + 2``) but the return value is not guaranteed + - Old code generator: ``3`` (``1 + 2``) but the return value is unspecified in general + - New code generator: ``4`` (``2 + 2``) but the return value is not guaranteed .. index:: ! evaluation order; function arguments @@ -247,7 +249,7 @@ hiding new and different behavior in existing code. } } - The function `f()` behaves as follows: + The function ``f()`` behaves as follows: - Old code generator: runs out of gas while zeroing the array contents after the large memory allocation - New code generator: reverts due to free memory pointer overflow (does not run out of gas) diff --git a/docs/language-influences.rst b/docs/language-influences.rst index e4f0ab6902..a3ab1562e9 100644 --- a/docs/language-influences.rst +++ b/docs/language-influences.rst @@ -2,7 +2,7 @@ Language Influences ################### -Solidity is a `curly-bracket language `_ +Solidity is a `curly-bracket language `_ that has been influenced and inspired by several well-known programming languages. Solidity is most profoundly influenced by C++, but also borrowed concepts from languages like diff --git a/docs/layout-of-source-files.rst b/docs/layout-of-source-files.rst index 9a58478586..600f2ed0f4 100644 --- a/docs/layout-of-source-files.rst +++ b/docs/layout-of-source-files.rst @@ -40,7 +40,7 @@ The comment is recognized by the compiler anywhere in the file at the file level, but it is recommended to put it at the top of the file. More information about how to use SPDX license identifiers -can be found at the `SPDX website `_. +can be found at the `SPDX website `_. .. index:: ! pragma @@ -182,7 +182,7 @@ Syntax and Semantics Solidity supports import statements to help modularise your code that are similar to those available in JavaScript (from ES6 on). However, Solidity does not support the concept of -a `default export `_. +a `default export `_. At a global level, you can use import statements of the following form: diff --git a/docs/logo.svg b/docs/logo.svg index 86b9f4995b..19391843b4 100644 --- a/docs/logo.svg +++ b/docs/logo.svg @@ -1,27 +1,8 @@ - - - - -Vector 1 -Created with Sketch. - - - - - - - - - - - - + + + + + + + diff --git a/docs/make.bat b/docs/make.bat index bc06e706e0..d11deb3ffc 100644 --- a/docs/make.bat +++ b/docs/make.bat @@ -28,6 +28,7 @@ if "%1" == "help" ( 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. text to make text files echo. man to make manual pages echo. texinfo to make Texinfo files @@ -155,16 +156,6 @@ if "%1" == "latexpdf" ( goto end ) -if "%1" == "latexpdfja" ( - %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex - cd %BUILDDIR%/latex - make all-pdf-ja - cd %BUILDDIR%/.. - 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 diff --git a/docs/metadata.rst b/docs/metadata.rst index 23871ba09e..a44d987538 100644 --- a/docs/metadata.rst +++ b/docs/metadata.rst @@ -6,18 +6,19 @@ Contract Metadata .. index:: metadata, contract verification -The Solidity compiler automatically generates a JSON file, the contract -metadata, that contains information about the compiled contract. You can use -this file to query the compiler version, the sources used, the ABI and NatSpec -documentation to more safely interact with the contract and verify its source -code. +The Solidity compiler automatically generates a JSON file. +The file contains two kinds of information about the compiled contract: + +- How to interact with the contract: ABI, and NatSpec documentation. +- How to reproduce the compilation and verify a deployed contract: + compiler version, compiler settings, and source files used. The compiler appends by default the IPFS hash of the metadata file to the end -of the bytecode (for details, see below) of each contract, so that you can -retrieve the file in an authenticated way without having to resort to a -centralized data provider. The other available options are the Swarm hash and -not appending the metadata hash to the bytecode. These can be configured via -the :ref:`Standard JSON Interface`. +of the runtime bytecode (not necessarily the creation bytecode) of each contract, +so that, if published, you can retrieve the file in an authenticated way without +having to resort to a centralized data provider. The other available options are +the Swarm hash and not appending the metadata hash to the bytecode. These can be +configured via the :ref:`Standard JSON Interface`. You have to publish the metadata file to IPFS, Swarm, or another service so that others can access it. You create the file by using the ``solc --metadata`` @@ -30,108 +31,50 @@ shall match with the one contained in the bytecode. The metadata file has the following format. The example below is presented in a human-readable way. Properly formatted metadata should use quotes correctly, -reduce whitespace to a minimum and sort the keys of all objects to arrive at a -unique formatting. Comments are not permitted and used here only for +reduce whitespace to a minimum, and sort the keys of all objects in alphabetical order +to arrive at a canonical formatting. Comments are not permitted and are used here only for explanatory purposes. .. code-block:: javascript { - // Required: The version of the metadata format - "version": "1", - // Required: Source code language, basically selects a "sub-version" - // of the specification - "language": "Solidity", // Required: Details about the compiler, contents are specific // to the language. "compiler": { - // Required for Solidity: Version of the compiler - "version": "0.8.2+commit.661d1103", // Optional: Hash of the compiler binary which produced this output - "keccak256": "0x123..." - }, - // Required: Compilation source files/source units, keys are file paths - "sources": - { - "myDirectory/myFile.sol": { - // Required: keccak256 hash of the source file - "keccak256": "0x123...", - // Required (unless "content" is used, see below): Sorted URL(s) - // to the source file, protocol is more or less arbitrary, but an - // IPFS URL is recommended - "urls": [ "bzz-raw://7d7a...", "dweb:/ipfs/QmN..." ], - // Optional: SPDX license identifier as given in the source file - "license": "MIT" - }, - "destructible": { - // Required: keccak256 hash of the source file - "keccak256": "0x234...", - // Required (unless "url" is used): literal contents of the source file - "content": "contract destructible is owned { function destroy() { if (msg.sender == owner) selfdestruct(owner); } }" - } - }, - // Required: Compiler settings - "settings": - { - // Required for Solidity: Sorted list of import remappings - "remappings": [ ":g=/dir" ], - // Optional: Optimizer settings. The fields "enabled" and "runs" are deprecated - // and are only given for backwards-compatibility. - "optimizer": { - "enabled": true, - "runs": 500, - "details": { - // peephole defaults to "true" - "peephole": true, - // inliner defaults to "true" - "inliner": true, - // jumpdestRemover defaults to "true" - "jumpdestRemover": true, - "orderLiterals": false, - "deduplicate": false, - "cse": false, - "constantOptimizer": false, - "yul": true, - // Optional: Only present if "yul" is "true" - "yulDetails": { - "stackAllocation": false, - "optimizerSteps": "dhfoDgvulfnTUtnIf..." - } - } - }, - "metadata": { - // Reflects the setting used in the input json, defaults to "true" - "appendCBOR": true, - // Reflects the setting used in the input json, defaults to "false" - "useLiteralContent": true, - // Reflects the setting used in the input json, defaults to "ipfs" - "bytecodeHash": "ipfs" - }, - // Required for Solidity: File path and the name of the contract or library this - // metadata is created for. - "compilationTarget": { - "myDirectory/myFile.sol": "MyContract" - }, - // Required for Solidity: Addresses for libraries used - "libraries": { - "MyLib": "0x123123..." - } + "keccak256": "0x123...", + // Required for Solidity: Version of the compiler + "version": "0.8.2+commit.661d1103" }, + // Required: Source code language, basically selects a "sub-version" + // of the specification + "language": "Solidity", // Required: Generated information about the contract. - "output": - { + "output": { // Required: ABI definition of the contract. See "Contract ABI Specification" "abi": [/* ... */], - // Required: NatSpec developer documentation of the contract. + // Required: NatSpec developer documentation of the contract. See https://docs.soliditylang.org/en/latest/natspec-format.html for details. "devdoc": { - "version": 1 // NatSpec version - "kind": "dev", // Contents of the @author NatSpec field of the contract "author": "John Doe", - // Contents of the @title NatSpec field of the contract - "title": "MyERC20: an example ERC20" // Contents of the @dev NatSpec field of the contract "details": "Interface of the ERC20 standard as defined in the EIP. See https://eips.ethereum.org/EIPS/eip-20 for details", + "errors": { + "MintToZeroAddress()" : { + "details": "Cannot mint to zero address" + } + }, + "events": { + "Transfer(address,address,uint256)": { + "details": "Emitted when `value` tokens are moved from one account (`from`) toanother (`to`).", + "params": { + "from": "The sender address", + "to": "The receiver address", + "value": "The token amount" + } + } + }, + "kind": "dev", "methods": { "transfer(address,uint256)": { // Contents of the @dev NatSpec field of the method @@ -140,7 +83,7 @@ explanatory purposes. "params": { "_value": "The amount tokens to be transferred", "_to": "The receiver address" - } + }, // Contents of the @return NatSpec field. "returns": { // Return var name (here "success") if exists. "_0" as key if return var is unnamed @@ -153,34 +96,104 @@ explanatory purposes. // Contents of the @dev NatSpec field of the state variable "details": "Must be set during contract creation. Can then only be changed by the owner" } - } - "events": { - "Transfer(address,address,uint256)": { - "details": "Emitted when `value` tokens are moved from one account (`from`) toanother (`to`)." - "params": { - "from": "The sender address" - "to": "The receiver address" - "value": "The token amount" - } - } - } + }, + // Contents of the @title NatSpec field of the contract + "title": "MyERC20: an example ERC20", + "version": 1 // NatSpec version }, - // Required: NatSpec user documentation of the contract + // Required: NatSpec user documentation of the contract. See "NatSpec Format" "userdoc": { - "version": 1 // NatSpec version + "errors": { + "ApprovalCallerNotOwnerNorApproved()": [ + { + "notice": "The caller must own the token or be an approved operator." + } + ] + }, + "events": { + "Transfer(address,address,uint256)": { + "notice": "`_value` tokens have been moved from `from` to `to`" + } + }, "kind": "user", "methods": { "transfer(address,uint256)": { "notice": "Transfers `_value` tokens to address `_to`" } }, - "events": { - "Transfer(address,address,uint256)": { - "notice": "`_value` tokens have been moved from `from` to `to`" + "version": 1 // NatSpec version + } + }, + // Required: Compiler settings. Reflects the settings in the JSON input during compilation. + // Check the documentation of standard JSON input's "settings" field + "settings": { + // Required for Solidity: File path and the name of the contract or library this + // metadata is created for. + "compilationTarget": { + "myDirectory/myFile.sol": "MyContract" + }, + // Required for Solidity. + "evmVersion": "london", + // Required for Solidity: Addresses for libraries used. + "libraries": { + "MyLib": "0x123123..." + }, + "metadata": { + // Reflects the setting used in the input json, defaults to "true" + "appendCBOR": true, + // Reflects the setting used in the input json, defaults to "ipfs" + "bytecodeHash": "ipfs", + // Reflects the setting used in the input json, defaults to "false" + "useLiteralContent": true + }, + // Optional: Optimizer settings. The fields "enabled" and "runs" are deprecated + // and are only given for backward-compatibility. + "optimizer": { + "details": { + "constantOptimizer": false, + "cse": false, + "deduplicate": false, + // inliner defaults to "false" + "inliner": false, + // jumpdestRemover defaults to "true" + "jumpdestRemover": true, + "orderLiterals": false, + // peephole defaults to "true" + "peephole": true, + "yul": true, + // Optional: Only present if "yul" is "true" + "yulDetails": { + "optimizerSteps": "dhfoDgvulfnTUtnIf...", + "stackAllocation": false } - } + }, + "enabled": true, + "runs": 500 + }, + // Required for Solidity: Sorted list of import remappings. + "remappings": [ ":g=/dir" ] + }, + // Required: Compilation source files/source units, keys are file paths + "sources": { + "settable": { + // Required (unless "url" is used): literal contents of the source file + "content": "contract settable is owned { uint256 private x = 0; function set(uint256 _x) public { if (msg.sender == owner) x = _x; } }", + // Required: keccak256 hash of the source file + "keccak256": "0x234..." + }, + "myDirectory/myFile.sol": { + // Required: keccak256 hash of the source file + "keccak256": "0x123...", + // Optional: SPDX license identifier as given in the source file + "license": "MIT", + // Required (unless "content" is used, see above): Sorted URL(s) + // to the source file, protocol is more or less arbitrary, but an + // IPFS URL is recommended + "urls": [ "bzz-raw://7d7a...", "dweb:/ipfs/QmN..." ] } - } + }, + // Required: The version of the metadata format + "version": 1 } .. warning:: @@ -200,26 +213,35 @@ explanatory purposes. Encoding of the Metadata Hash in the Bytecode ============================================= -Because we might support other ways to retrieve the metadata file in the future, -the mapping ``{"ipfs": , "solc": }`` is stored -`CBOR `_-encoded. Since the mapping might -contain more keys (see below) and the beginning of that -encoding is not easy to find, its length is added in a two-byte big-endian -encoding. The current version of the Solidity compiler usually adds the following -to the end of the deployed bytecode +The compiler currently by default appends the +`IPFS hash (in CID v0) `_ +of the canonical metadata file and the compiler version to the end of the bytecode. +Optionally, a Swarm hash instead of the IPFS, or an experimental flag is used. +Below are all the possible fields: -.. code-block:: text +.. code-block:: javascript - 0xa2 - 0x64 'i' 'p' 'f' 's' 0x58 0x22 <34 bytes IPFS hash> - 0x64 's' 'o' 'l' 'c' 0x43 <3 byte version encoding> - 0x00 0x33 + { + "ipfs": "", + // If "bytecodeHash" was "bzzr1" in compiler settings not "ipfs" but "bzzr1" + "bzzr1": "", + // Previous versions were using "bzzr0" instead of "bzzr1" + "bzzr0": "", + // If any experimental features that affect code generation are used + "experimental": true, + "solc": "" + } -So in order to retrieve the data, the end of the deployed bytecode can be checked -to match that pattern and the IPFS hash can be used to retrieve the file (if pinned/published). +Because we might support other ways to retrieve the +metadata file in the future, this information is stored +`CBOR `_-encoded. The last two bytes in the bytecode +indicate the length of the CBOR encoded information. By looking at this length, the +relevant part of the bytecode can be decoded with a CBOR decoder. + +Check the `Metadata Playground `_ to see it in action. Whereas release builds of solc use a 3 byte encoding of the version as shown -above (one byte each for major, minor and patch version number), prerelease builds +above (one byte each for major, minor and patch version number), pre-release builds will instead use a complete version string including commit hash and build date. The commandline flag ``--no-cbor-metadata`` can be used to skip metadata @@ -228,17 +250,9 @@ boolean field ``settings.metadata.appendCBOR`` in Standard JSON input can be set .. note:: The CBOR mapping can also contain other keys, so it is better to fully - decode the data instead of relying on it starting with ``0xa264``. - For example, if any experimental features that affect code generation - are used, the mapping will also contain ``"experimental": true``. - -.. note:: - The compiler currently uses the IPFS hash of the metadata by default, but - it may also use the bzzr1 hash or some other hash in the future, so do - not rely on this sequence to start with ``0xa2 0x64 'i' 'p' 'f' 's'``. We - might also add additional data to this CBOR structure, so the best option - is to use a proper CBOR parser. - + decode the data by looking at the end of the bytecode for the CBOR length, + and to use a proper CBOR parser. Do not rely on it starting with ``0xa264`` + or ``0xa2 0x64 'i' 'p' 'f' 's'``. Usage for Automatic Interface Generation and NatSpec ==================================================== @@ -252,24 +266,27 @@ is JSON-decoded into a structure like above. The component can then use the ABI to automatically generate a rudimentary user interface for the contract. -Furthermore, the wallet can use the NatSpec user documentation to display a human-readable confirmation message to the user -whenever they interact with the contract, together with requesting -authorization for the transaction signature. +Furthermore, the wallet can use the NatSpec user documentation to display a +human-readable confirmation message to the user whenever they interact with +the contract, together with requesting authorization for the transaction signature. For additional information, read :doc:`Ethereum Natural Language Specification (NatSpec) format `. Usage for Source Code Verification ================================== -In order to verify the compilation, sources can be retrieved from IPFS/Swarm -via the link in the metadata file. -The compiler of the correct version (which is checked to be part of the "official" compilers) -is invoked on that input with the specified settings. The resulting -bytecode is compared to the data of the creation transaction or ``CREATE`` opcode data. -This automatically verifies the metadata since its hash is part of the bytecode. -Excess data corresponds to the constructor input data, which should be decoded -according to the interface and presented to the user. +If pinned/published, it is possible to retrieve the metadata of the contract from IPFS/Swarm. +The metadata file also contains the URLs or the IPFS hashes of the source files, as well as +the compilation settings, i.e. everything needed to reproduce a compilation. + +With this information it is then possible to verify the source code of a contract by +reproducing the compilation, and comparing the bytecode from the compilation with +the bytecode of the deployed contract. + +This automatically verifies the metadata since its hash is part of the bytecode, as well +as the source codes, because their hashes are part of the metadata. Any change in the files +or settings would result in a different metadata hash. The metadata here serves +as a fingerprint of the whole compilation. -In the repository `sourcify `_ -(`npm package `_) you can see -example code that shows how to use this feature. +`Sourcify `_ makes use of this feature for "full/perfect verification", +as well as pinning the files publicly on IPFS to be accessed with the metadata hash. diff --git a/docs/natspec-format.rst b/docs/natspec-format.rst index 01726fe860..f191064837 100644 --- a/docs/natspec-format.rst +++ b/docs/natspec-format.rst @@ -46,7 +46,7 @@ for the purposes of NatSpec. - For Vyper, use ``"""`` indented to the inner contents with bare comments. See the `Vyper - documentation `__. + documentation `__. The following example shows a contract and a function using all available tags. @@ -58,7 +58,7 @@ The following example shows a contract and a function using all available tags. This may change in the future. -.. code-block:: Solidity +.. code-block:: solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.2 < 0.9.0; @@ -73,8 +73,9 @@ The following example shows a contract and a function using all available tags. /// @dev The Alexandr N. Tetearing algorithm could increase precision /// @param rings The number of rings from dendrochronological sample /// @return Age in years, rounded up for partial years - function age(uint256 rings) external virtual pure returns (uint256) { - return rings + 1; + /// @return Name of the tree + function age(uint256 rings) external virtual pure returns (uint256, string memory) { + return (rings + 1, "tree"); } /// @notice Returns the amount of leaves the tree has. @@ -91,8 +92,8 @@ The following example shows a contract and a function using all available tags. } contract KumquatTree is Tree, Plant { - function age(uint256 rings) external override pure returns (uint256) { - return rings + 2; + function age(uint256 rings) external override pure returns (uint256, string memory) { + return (rings + 2, "Kumquat"); } /// Return the amount of leaves that this specific kind of tree has @@ -115,11 +116,11 @@ in the same way as if it were tagged with ``@notice``. =============== ====================================================================================== ============================= Tag Context =============== ====================================================================================== ============================= -``@title`` A title that should describe the contract/interface contract, library, interface -``@author`` The name of the author contract, library, interface -``@notice`` Explain to an end user what this does contract, library, interface, function, public state variable, event -``@dev`` Explain to a developer any extra details contract, library, interface, function, state variable, event -``@param`` Documents a parameter just like in Doxygen (must be followed by parameter name) function, event +``@title`` A title that should describe the contract/interface contract, library, interface, struct, enum +``@author`` The name of the author contract, library, interface, struct, enum +``@notice`` Explain to an end user what this does contract, library, interface, function, public state variable, event, struct, enum, error +``@dev`` Explain to a developer any extra details contract, library, interface, function, state variable, event, struct, enum, error +``@param`` Documents a parameter just like in Doxygen (must be followed by parameter name) function, event, error ``@return`` Documents the return variables of a contract's function function, public state variable ``@inheritdoc`` Copies all missing tags from the base function (must be followed by the contract name) function, public state variable ``@custom:...`` Custom tag, semantics is application-defined everywhere @@ -196,7 +197,7 @@ User Documentation ------------------ The above documentation will produce the following user documentation -JSON file as output: +JSON file as output for the ``Tree`` contract: .. code-block:: json @@ -209,6 +210,10 @@ JSON file as output: { "notice" : "Calculate tree age in years, rounded up, for live trees" } + "leaves()" : + { + "notice" : "Returns the amount of leaves the tree has." + } }, "notice" : "You can use this contract for only the most basic simulation" } @@ -243,7 +248,14 @@ file should also be produced and should look like this: { "rings" : "The number of rings from dendrochronological sample" }, - "return" : "age in years, rounded up for partial years" + "returns" : { + "_0" : "Age in years, rounded up for partial years", + "_1" : "Name of the tree" + } + }, + "leaves()" : + { + "details" : "Returns only a fixed number." } }, "title" : "A simulator for trees" diff --git a/docs/path-resolution.rst b/docs/path-resolution.rst index c61abce0a2..ff4930bc9d 100644 --- a/docs/path-resolution.rst +++ b/docs/path-resolution.rst @@ -21,7 +21,7 @@ source unit is assigned a unique *source unit name* which is an opaque and unstr When you use the :ref:`import statement `, you specify an *import path* that references a source unit name. -.. index:: ! import callback, ! Host Filesystem Loader +.. index:: ! import callback, ! Host Filesystem Loader, ! --no-import-callback .. _import-callback: Import Callback @@ -36,11 +36,12 @@ An import callback is free to interpret source unit names in an arbitrary way, n If there is no callback available when one is needed or if it fails to locate the source code, compilation fails. -The command-line compiler provides the *Host Filesystem Loader* - a rudimentary callback +By default, the command-line compiler provides the *Host Filesystem Loader* - a rudimentary callback that interprets a source unit name as a path in the local filesystem. +This callback can be disabled using the ``--no-import-callback`` command-line option. The `JavaScript interface `_ does not provide any by default, but one can be provided by the user. -This mechanism can be used to obtain source code from locations other then the local filesystem +This mechanism can be used to obtain source code from locations other than the local filesystem (which may not even be accessible, e.g. when the compiler is running in a browser). For example the `Remix IDE `_ provides a versatile callback that lets you `import files from HTTP, IPFS and Swarm URLs or refer directly to packages in NPM registry @@ -139,7 +140,7 @@ The initial content of the VFS depends on how you invoke the compiler: #. **Standard input** - On the command line it is also possible to provide the source by sending it to compiler's + On the command-line it is also possible to provide the source by sending it to compiler's standard input: .. code-block:: bash @@ -253,6 +254,7 @@ The compiler resolves the import into a source unit name based on the import pat #. We start with the source unit name of the importing source unit. #. The last path segment with preceding slashes is removed from the resolved name. #. Then, for every segment in the import path, starting from the leftmost one: + - If the segment is ``.``, it is skipped. - If the segment is ``..``, the last path segment with preceding slashes is removed from the resolved name. - Otherwise, the segment (preceded by a single slash if the resolved name is not empty), is appended to the resolved name. @@ -344,13 +346,13 @@ of the compiler. CLI Path Normalization and Stripping ------------------------------------ -On the command line the compiler behaves just as you would expect from any other program: +On the command-line the compiler behaves just as you would expect from any other program: it accepts paths in a format native to the platform and relative paths are relative to the current working directory. -The source unit names assigned to files whose paths are specified on the command line, however, +The source unit names assigned to files whose paths are specified on the command-line, however, should not change just because the project is being compiled on a different platform or because the compiler happens to have been invoked from a different directory. -To achieve this, paths to source files coming from the command line must be converted to a canonical +To achieve this, paths to source files coming from the command-line must be converted to a canonical form, and, if possible, made relative to the base path or one of the include paths. The normalization rules are as follows: @@ -407,7 +409,7 @@ The resulting file path becomes the source unit name. Prior to version 0.8.8, CLI path stripping was not performed and the only normalization applied was the conversion of path separators. When working with older versions of the compiler it is recommended to invoke the compiler from - the base path and to only use relative paths on the command line. + the base path and to only use relative paths on the command-line. .. index:: ! allowed paths, ! --allow-paths, remapping; target .. _allowed-paths: @@ -420,7 +422,7 @@ locations that are considered safe by default: - Outside of Standard JSON mode: - - The directories containing input files listed on the command line. + - The directories containing input files listed on the command-line. - The directories used as :ref:`remapping ` targets. If the target is not a directory (i.e does not end with ``/``, ``/.`` or ``/..``) the directory containing the target is used instead. @@ -514,7 +516,7 @@ you can use the following in your source file: The compiler will look for the file in the VFS under ``dapp-bin/library/math.sol``. If the file is not available there, the source unit name will be passed to the Host Filesystem -Loader, which will then look in ``/project/dapp-bin/library/iterable_mapping.sol``. +Loader, which will then look in ``/project/dapp-bin/library/math.sol``. .. warning:: @@ -550,7 +552,7 @@ you checked out to ``/project/dapp-bin_old``, then you can run: This means that all imports in ``module2`` point to the old version but imports in ``module1`` point to the new version. -Here are the detailed rules governing the behaviour of remappings: +Here are the detailed rules governing the behavior of remappings: #. **Remappings only affect the translation between import paths and source unit names.** diff --git a/docs/requirements.txt b/docs/requirements.txt index 23c8f90432..b1b21a8691 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -4,7 +4,7 @@ sphinx_rtd_theme>=0.5.2 pygments-lexer-solidity>=0.7.0 -sphinx-a4doc>=1.2.1 +sphinx-a4doc>=1.6.0 # Sphinx 2.1.0 is the oldest version that accepts a lexer class in add_lexer() -sphinx>=2.1.0 +sphinx>=2.1.0, <6.0 diff --git a/docs/resources.rst b/docs/resources.rst index 3b403cd6d0..1773859d4e 100644 --- a/docs/resources.rst +++ b/docs/resources.rst @@ -23,12 +23,9 @@ Integrated (Ethereum) Development Environments Python-based development and testing framework for smart contracts targeting the Ethereum Virtual Machine. * `Dapp `_ - Tool for building, testing and deploying smart contracts from the command line. + Tool for building, testing and deploying smart contracts from the command-line. - * `Embark `_ - Developer platform for building and deploying decentralized applications. - - * `Foundry `_ + * `Foundry `_ Fast, portable and modular toolkit for Ethereum application development written in Rust. * `Hardhat `_ @@ -37,23 +34,12 @@ Integrated (Ethereum) Development Environments * `Remix `_ Browser-based IDE with integrated compiler and Solidity runtime environment without server-side components. - * `Truffle `_ + * `Truffle `_ Ethereum development framework. Editor Integrations =================== -* Atom - - * `Etheratom `_ - Plugin for the Atom editor that features syntax highlighting, compilation and a runtime environment (Backend node & VM compatible). - - * `Atom Solidity Linter `_ - Plugin for the Atom editor that provides Solidity linting. - - * `Atom Solium Linter `_ - Configurable Solidity linter for Atom using Solium (now Ethlint) as a base. - * Emacs * `Emacs Solidity `_ @@ -61,32 +47,41 @@ Editor Integrations * IntelliJ - * `IntelliJ IDEA plugin `_ - Solidity plugin for IntelliJ IDEA (and all other JetBrains IDEs) + * `IntelliJ IDEA plugin `_ + Solidity plugin for IntelliJ IDEA (and all other JetBrains IDEs). -* Sublime +* Sublime Text * `Package for SublimeText - Solidity language syntax `_ Solidity syntax highlighting for SublimeText editor. * Vim - * `Vim Solidity `_ - Plugin for the Vim editor providing syntax highlighting. + * `Vim Solidity by Thesis `_ + Syntax highlighting for Solidity in Vim. + + * `Vim Solidity by TovarishFin `_ + Vim syntax file for Solidity. * `Vim Syntastic `_ Plugin for the Vim editor providing compile checking. -* Visual Studio Code +* Visual Studio Code (VS Code) + + * `Ethereum Remix Visual Studio Code extension `_ + Ethereum Remix extension pack for VS Code - * `Visual Studio Code extension `_ + * `Solidity Visual Studio Code extension, by Juan Blanco `_ Solidity plugin for Microsoft Visual Studio Code that includes syntax highlighting and the Solidity compiler. + * `Solidity Visual Studio Code extension, by Nomic Foundation `_ + Solidity and Hardhat support by the Hardhat team, including: syntax highlighting, jump to definition, renames, quick fixes and inline solc warnings and errors. + * `Solidity Visual Auditor extension `_ Adds security centric syntax and semantic highlighting to Visual Studio Code. - * `Hardhat + Solidity Visual Studio Code extension `_ - Solidity and Hardhat support by the Hardhat team. + * `Truffle for VS Code `_ + Build, debug and deploy smart contracts on Ethereum and EVM-compatible blockchains. Solidity Tools ============== @@ -115,10 +110,7 @@ Solidity Tools * `leafleth `_ A documentation generator for Solidity smart-contracts. -* `PIET `_ - A tool to develop, audit and use Solidity smart contracts through a simple graphical interface. - -* `Scaffold-ETH `_ +* `Scaffold-ETH 2 `_ Forkable Ethereum development stack focused on fast product iterations. * `sol2uml `_ diff --git a/docs/security-considerations.rst b/docs/security-considerations.rst index 0375484316..92d601043d 100644 --- a/docs/security-considerations.rst +++ b/docs/security-considerations.rst @@ -7,28 +7,28 @@ Security Considerations While it is usually quite easy to build software that works as expected, it is much harder to check that nobody can use it in a way that was **not** anticipated. -In Solidity, this is even more important because you can use smart contracts -to handle tokens or, possibly, even more valuable things. Furthermore, every -execution of a smart contract happens in public and, in addition to that, -the source code is often available. - -Of course you always have to consider how much is at stake: -You can compare a smart contract with a web service that is open to the -public (and thus, also to malicious actors) and perhaps even open source. -If you only store your grocery list on that web service, you might not have -to take too much care, but if you manage your bank account using that web service, -you should be more careful. - -This section will list some pitfalls and general security recommendations but -can, of course, never be complete. Also, keep in mind that even if your smart -contract code is bug-free, the compiler or the platform itself might have a -bug. A list of some publicly known security-relevant bugs of the compiler can -be found in the :ref:`list of known bugs`, which is also -machine-readable. Note that there is a bug bounty program that covers the code -generator of the Solidity compiler. - -As always, with open source documentation, please help us extend this section -(especially, some examples would not hurt)! +In Solidity, this is even more important because you can use smart contracts to handle tokens or, +possibly, even more valuable things. +Furthermore, every execution of a smart contract happens in public and, +in addition to that, the source code is often available. + +Of course, you always have to consider how much is at stake: +You can compare a smart contract with a web service that is open to the public +(and thus, also to malicious actors) and perhaps even open-source. +If you only store your grocery list on that web service, you might not have to take too much care, +but if you manage your bank account using that web service, you should be more careful. + +This section will list some pitfalls and general security recommendations +but can, of course, never be complete. +Also, keep in mind that even if your smart contract code is bug-free, +the compiler or the platform itself might have a bug. +A list of some publicly known security-relevant bugs of the compiler can be found +in the :ref:`list of known bugs`, which is also machine-readable. +Note that there is a `Bug Bounty Program `_ +that covers the code generator of the Solidity compiler. + +As always, with open-source documentation, +please help us extend this section (especially, some examples would not hurt)! NOTE: In addition to the list below, you can find more security recommendations and best practices `in Guy Lando's knowledge list `_ and @@ -41,20 +41,18 @@ Pitfalls Private Information and Randomness ================================== -Everything you use in a smart contract is publicly visible, even -local variables and state variables marked ``private``. +Everything you use in a smart contract is publicly visible, +even local variables and state variables marked ``private``. -Using random numbers in smart contracts is quite tricky if you do not want -block builders to be able to cheat. +Using random numbers in smart contracts is quite tricky if you do not want block builders to be able to cheat. -Re-Entrancy -=========== +Reentrancy +========== -Any interaction from a contract (A) with another contract (B) and any transfer -of Ether hands over control to that contract (B). This makes it possible for B -to call back into A before this interaction is completed. To give an example, -the following code contains a bug (it is just a snippet and not a -complete contract): +Any interaction from a contract (A) with another contract (B) +and any transfer of Ether hands over control to that contract (B). +This makes it possible for B to call back into A before this interaction is completed. +To give an example, the following code contains a bug (it is just a snippet and not a complete contract): .. code-block:: solidity @@ -72,12 +70,12 @@ complete contract): } } -The problem is not too serious here because of the limited gas as part -of ``send``, but it still exposes a weakness: Ether transfer can always -include code execution, so the recipient could be a contract that calls -back into ``withdraw``. This would let it get multiple refunds and -basically retrieve all the Ether in the contract. In particular, the -following contract will allow an attacker to refund multiple times +The problem is not too serious here because of the limited gas as part of ``send``, +but it still exposes a weakness: +Ether transfer can always include code execution, +so the recipient could be a contract that calls back into ``withdraw``. +This would let it get multiple refunds and, basically, retrieve all the Ether in the contract. +In particular, the following contract will allow an attacker to refund multiple times as it uses ``call`` which forwards all remaining gas by default: .. code-block:: solidity @@ -97,8 +95,7 @@ as it uses ``call`` which forwards all remaining gas by default: } } -To avoid re-entrancy, you can use the Checks-Effects-Interactions pattern as -demonstrated below: +To avoid reentrancy, you can use the Checks-Effects-Interactions pattern as demonstrated below: .. code-block:: solidity @@ -116,58 +113,58 @@ demonstrated below: } } -The Checks-Effects-Interactions pattern ensures that all code paths through a contract complete all required checks -of the supplied parameters before modifying the contract's state (Checks); only then it makes any changes to the state (Effects); -it may make calls to functions in other contracts *after* all planned state changes have been written to -storage (Interactions). This is a common foolproof way to prevent *re-entrancy attacks*, where an externally called -malicious contract is able to double-spend an allowance, double-withdraw a balance, among other things, by using logic that calls back into the -original contract before it has finalized its transaction. - -Note that re-entrancy is not only an effect of Ether transfer but of any -function call on another contract. Furthermore, you also have to take -multi-contract situations into account. A called contract could modify the -state of another contract you depend on. +The Checks-Effects-Interactions pattern ensures that all code paths through a contract +complete all required checks of the supplied parameters before modifying the contract's state (Checks); +only then it makes any changes to the state (Effects); +it may make calls to functions in other contracts +*after* all planned state changes have been written to storage (Interactions). +This is a common foolproof way to prevent *reentrancy attacks*, +where an externally called malicious contract can double-spend an allowance, +double-withdraw a balance, among other things, +by using logic that calls back into the original contract before it has finalized its transaction. + +Note that reentrancy is not only an effect of Ether transfer +but of any function call on another contract. +Furthermore, you also have to take multi-contract situations into account. +A called contract could modify the state of another contract you depend on. Gas Limit and Loops =================== -Loops that do not have a fixed number of iterations, for example, loops that depend on storage values, have to be used carefully: -Due to the block gas limit, transactions can only consume a certain amount of gas. Either explicitly or just due to -normal operation, the number of iterations in a loop can grow beyond the block gas limit which can cause the complete -contract to be stalled at a certain point. This may not apply to ``view`` functions that are only executed -to read data from the blockchain. Still, such functions may be called by other contracts as part of on-chain operations -and stall those. Please be explicit about such cases in the documentation of your contracts. +Loops that do not have a fixed number of iterations, for example, +loops that depend on storage values, have to be used carefully: +Due to the block gas limit, transactions can only consume a certain amount of gas. +Either explicitly or just due to normal operation, +the number of iterations in a loop can grow beyond the block gas limit +which can cause the complete contract to be stalled at a certain point. +This may not apply to ``view`` functions that are only executed to read data from the blockchain. +Still, such functions may be called by other contracts as part of on-chain operations and stall those. +Please be explicit about such cases in the documentation of your contracts. Sending and Receiving Ether =========================== -- Neither contracts nor "external accounts" are currently able to prevent that someone sends them Ether. - Contracts can react on and reject a regular transfer, but there are ways - to move Ether without creating a message call. One way is to simply "mine to" - the contract address and the second way is using ``selfdestruct(x)``. +- Neither contracts nor "external accounts" are currently able to prevent someone from sending them Ether. + Contracts can react on and reject a regular transfer, but there are ways to move Ether without creating a message call. + One way is to simply "mine to" the contract address and the second way is using ``selfdestruct(x)``. -- If a contract receives Ether (without a function being called), - either the :ref:`receive Ether ` +- If a contract receives Ether (without a function being called), either the :ref:`receive Ether ` or the :ref:`fallback ` function is executed. - If it does not have a receive nor a fallback function, the Ether will be - rejected (by throwing an exception). During the execution of one of these - functions, the contract can only rely on the "gas stipend" it is passed (2300 - gas) being available to it at that time. This stipend is not enough to modify - storage (do not take this for granted though, the stipend might change with - future hard forks). To be sure that your contract can receive Ether in that - way, check the gas requirements of the receive and fallback functions + If it does not have a ``receive`` nor a ``fallback`` function, the Ether will be rejected (by throwing an exception). + During the execution of one of these functions, the contract can only rely on the "gas stipend" it is passed (2300 gas) + being available to it at that time. + This stipend is not enough to modify storage (do not take this for granted though, the stipend might change with future hard forks). + To be sure that your contract can receive Ether in that way, check the gas requirements of the receive and fallback functions (for example in the "details" section in Remix). -- There is a way to forward more gas to the receiving contract using - ``addr.call{value: x}("")``. This is essentially the same as ``addr.transfer(x)``, - only that it forwards all remaining gas and opens up the ability for the - recipient to perform more expensive actions (and it returns a failure code - instead of automatically propagating the error). This might include calling back - into the sending contract or other state changes you might not have thought of. +- There is a way to forward more gas to the receiving contract using ``addr.call{value: x}("")``. + This is essentially the same as ``addr.transfer(x)``, only that it forwards all remaining gas + and opens up the ability for the recipient to perform more expensive actions + (and it returns a failure code instead of automatically propagating the error). + This might include calling back into the sending contract or other state changes you might not have thought of. So it allows for great flexibility for honest users but also for malicious actors. -- Use the most precise units to represent the wei amount as possible, as you lose - any that is rounded due to a lack of precision. +- Use the most precise units to represent the Wei amount as possible, as you lose any that is rounded due to a lack of precision. - If you want to send Ether using ``address.transfer``, there are certain details to be aware of: @@ -191,24 +188,28 @@ Sending and Receiving Ether Call Stack Depth ================ -External function calls can fail any time because they exceed the maximum -call stack size limit of 1024. In such situations, Solidity throws an exception. +External function calls can fail at any time +because they exceed the maximum call stack size limit of 1024. +In such situations, Solidity throws an exception. Malicious actors might be able to force the call stack to a high value -before they interact with your contract. Note that, since `Tangerine Whistle `_ hardfork, the `63/64 rule `_ makes call stack depth attack impractical. Also note that the call stack and the expression stack are unrelated, even though both have a size limit of 1024 stack slots. +before they interact with your contract. +Note that, since `Tangerine Whistle `_ hardfork, +the `63/64 rule `_ makes call stack depth attack impractical. +Also note that the call stack and the expression stack are unrelated, +even though both have a size limit of 1024 stack slots. -Note that ``.send()`` does **not** throw an exception if the call stack is -depleted but rather returns ``false`` in that case. The low-level functions -``.call()``, ``.delegatecall()`` and ``.staticcall()`` behave in the same way. +Note that ``.send()`` does **not** throw an exception if the call stack is depleted +but rather returns ``false`` in that case. +The low-level functions ``.call()``, ``.delegatecall()`` and ``.staticcall()`` behave in the same way. Authorized Proxies ================== -If your contract can act as a proxy, i.e. if it can call arbitrary contracts -with user-supplied data, then the user can essentially assume the identity -of the proxy contract. Even if you have other protective measures in place, -it is best to build your contract system such that the proxy does not have -any permissions (not even for itself). If needed, you can accomplish that -using a second proxy: +If your contract can act as a proxy, i.e. if it can call arbitrary contracts with user-supplied data, +then the user can essentially assume the identity of the proxy contract. +Even if you have other protective measures in place, it is best to build your contract system such +that the proxy does not have any permissions (not even for itself). +If needed, you can accomplish that using a second proxy: .. code-block:: solidity @@ -236,7 +237,8 @@ using a second proxy: tx.origin ========= -Never use tx.origin for authorization. Let's say you have a wallet contract like this: +Never use ``tx.origin`` for authorization. +Let's say you have a wallet contract like this: .. code-block:: solidity @@ -279,7 +281,11 @@ Now someone tricks you into sending Ether to the address of this attack wallet: } } -If your wallet had checked ``msg.sender`` for authorization, it would get the address of the attack wallet, instead of the owner address. But by checking ``tx.origin``, it gets the original address that kicked off the transaction, which is still the owner address. The attack wallet instantly drains all your funds. +If your wallet had checked ``msg.sender`` for authorization, it would get the address of the attack wallet, +instead of the owner's address. +But by checking ``tx.origin``, it gets the original address that kicked off the transaction, +which is still the owner's address. +The attack wallet instantly drains all your funds. .. _underflow-overflow: @@ -319,16 +325,14 @@ Try to use ``require`` to limit the size of inputs to a reasonable range and use Clearing Mappings ================= -The Solidity type ``mapping`` (see :ref:`mapping-types`) is a storage-only -key-value data structure that does not keep track of the keys that were -assigned a non-zero value. Because of that, cleaning a mapping without extra -information about the written keys is not possible. -If a ``mapping`` is used as the base type of a dynamic storage array, deleting -or popping the array will have no effect over the ``mapping`` elements. The -same happens, for example, if a ``mapping`` is used as the type of a member -field of a ``struct`` that is the base type of a dynamic storage array. The -``mapping`` is also ignored in assignments of structs or arrays containing a -``mapping``. +The Solidity type ``mapping`` (see :ref:`mapping-types`) is a storage-only key-value data structure +that does not keep track of the keys that were assigned a non-zero value. +Because of that, cleaning a mapping without extra information about the written keys is not possible. +If a ``mapping`` is used as the base type of a dynamic storage array, +deleting or popping the array will have no effect over the ``mapping`` elements. +The same happens, for example, if a ``mapping`` is used as the type of a member field of a ``struct`` +that is the base type of a dynamic storage array. +The ``mapping`` is also ignored in assignments of structs or arrays containing a ``mapping``. .. code-block:: solidity @@ -336,7 +340,7 @@ field of a ``struct`` that is the base type of a dynamic storage array. The pragma solidity >=0.6.0 <0.9.0; contract Map { - mapping (uint => uint)[] array; + mapping(uint => uint)[] array; function allocate(uint newMaps) public { for (uint i = 0; i < newMaps; i++) @@ -356,15 +360,12 @@ field of a ``struct`` that is the base type of a dynamic storage array. The } } -Consider the example above and the following sequence of calls: ``allocate(10)``, -``writeMap(4, 128, 256)``. +Consider the example above and the following sequence of calls: ``allocate(10)``, ``writeMap(4, 128, 256)``. At this point, calling ``readMap(4, 128)`` returns 256. -If we call ``eraseMaps``, the length of state variable ``array`` is zeroed, but -since its ``mapping`` elements cannot be zeroed, their information stays alive -in the contract's storage. -After deleting ``array``, calling ``allocate(5)`` allows us to access -``array[4]`` again, and calling ``readMap(4, 128)`` returns 256 even without -another call to ``writeMap``. +If we call ``eraseMaps``, the length of the state variable ``array`` is zeroed, +but since its ``mapping`` elements cannot be zeroed, their information stays alive in the contract's storage. +After deleting ``array``, calling ``allocate(5)`` allows us to access ``array[4]`` again, +and calling ``readMap(4, 128)`` returns 256 even without another call to ``writeMap``. If your ``mapping`` information must be deleted, consider using a library similar to `iterable mapping `_, @@ -375,10 +376,11 @@ Minor Details - Types that do not occupy the full 32 bytes might contain "dirty higher order bits". This is especially important if you access ``msg.data`` - it poses a malleability risk: - You can craft transactions that call a function ``f(uint8 x)`` with a raw byte argument - of ``0xff000001`` and with ``0x00000001``. Both are fed to the contract and both will - look like the number ``1`` as far as ``x`` is concerned, but ``msg.data`` will - be different, so if you use ``keccak256(msg.data)`` for anything, you will get different results. + You can craft transactions that call a function ``f(uint8 x)`` + with a raw byte argument of ``0xff000001`` and with ``0x00000001``. + Both are fed to the contract and both will look like the number ``1`` as far as ``x`` is concerned, + but ``msg.data`` will be different, so if you use ``keccak256(msg.data)`` for anything, + you will get different results. *************** Recommendations @@ -388,48 +390,45 @@ Take Warnings Seriously ======================= If the compiler warns you about something, you should change it. -Even if you do not think that this particular warning has security -implications, there might be another issue buried beneath it. -Any compiler warning we issue can be silenced by slight changes to the -code. +Even if you do not think that this particular warning has security implications, +there might be another issue buried beneath it. +Any compiler warning we issue can be silenced by slight changes to the code. -Always use the latest version of the compiler to be notified about all recently -introduced warnings. +Always use the latest version of the compiler to be notified about all recently introduced warnings. -Messages of type ``info`` issued by the compiler are not dangerous, and simply -represent extra suggestions and optional information that the compiler thinks -might be useful to the user. +Messages of type ``info``, issued by the compiler, are not dangerous +and simply represent extra suggestions and optional information +that the compiler thinks might be useful to the user. Restrict the Amount of Ether ============================ -Restrict the amount of Ether (or other tokens) that can be stored in a smart -contract. If your source code, the compiler or the platform has a bug, these -funds may be lost. If you want to limit your loss, limit the amount of Ether. +Restrict the amount of Ether (or other tokens) that can be stored in a smart contract. +If your source code, the compiler or the platform has a bug, these funds may be lost. +If you want to limit your loss, limit the amount of Ether. Keep it Small and Modular ========================= -Keep your contracts small and easily understandable. Single out unrelated -functionality in other contracts or into libraries. General recommendations -about source code quality of course apply: Limit the amount of local variables, -the length of functions and so on. Document your functions so that others -can see what your intention was and whether it is different than what the code does. +Keep your contracts small and easily understandable. +Single out unrelated functionality in other contracts or into libraries. +General recommendations about the source code quality of course apply: +Limit the amount of local variables, the length of functions and so on. +Document your functions so that others can see what your intention was +and whether it is different than what the code does. Use the Checks-Effects-Interactions Pattern =========================================== -Most functions will first perform some checks (who called the function, -are the arguments in range, did they send enough Ether, does the person -have tokens, etc.). These checks should be done first. +Most functions will first perform some checks and they should be done first +(who called the function, are the arguments in range, did they send enough Ether, +does the person have tokens, etc.). -As the second step, if all checks passed, effects to the state variables -of the current contract should be made. Interaction with other contracts -should be the very last step in any function. +As the second step, if all checks passed, effects to the state variables of the current contract should be made. +Interaction with other contracts should be the very last step in any function. -Early contracts delayed some effects and waited for external function -calls to return in a non-error state. This is often a serious mistake -because of the re-entrancy problem explained above. +Early contracts delayed some effects and waited for external function calls to return in a non-error state. +This is often a serious mistake because of the reentrancy problem explained above. Note that, also, calls to known contracts might in turn cause calls to unknown contracts, so it is probably better to just always apply this pattern. @@ -437,24 +436,23 @@ unknown contracts, so it is probably better to just always apply this pattern. Include a Fail-Safe Mode ======================== -While making your system fully decentralised will remove any intermediary, -it might be a good idea, especially for new code, to include some kind -of fail-safe mechanism: +While making your system fully decentralized will remove any intermediary, +it might be a good idea, especially for new code, to include some kind of fail-safe mechanism: -You can add a function in your smart contract that performs some -self-checks like "Has any Ether leaked?", +You can add a function in your smart contract that performs some self-checks like "Has any Ether leaked?", "Is the sum of the tokens equal to the balance of the contract?" or similar things. -Keep in mind that you cannot use too much gas for that, so help through off-chain -computations might be needed there. +Keep in mind that you cannot use too much gas for that, +so help through off-chain computations might be needed there. -If the self-check fails, the contract automatically switches into some kind -of "failsafe" mode, which, for example, disables most of the features, hands over -control to a fixed and trusted third party or just converts the contract into -a simple "give me back my money" contract. +If the self-check fails, the contract automatically switches into some kind of "failsafe" mode, +which, for example, disables most of the features, +hands over control to a fixed and trusted third party +or just converts the contract into a simple "give me back my Ether" contract. Ask for Peer Review =================== The more people examine a piece of code, the more issues are found. -Asking people to review your code also helps as a cross-check to find out whether your code -is easy to understand - a very important criterion for good smart contracts. +Asking people to review your code also helps as a cross-check to find out +whether your code is easy to understand - +a very important criterion for good smart contracts. diff --git a/docs/smtchecker.rst b/docs/smtchecker.rst index b1a39ee17d..92aaa847c9 100644 --- a/docs/smtchecker.rst +++ b/docs/smtchecker.rst @@ -73,7 +73,7 @@ Tutorial Overflow ======== -.. code-block:: Solidity +.. code-block:: solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.0; @@ -97,7 +97,7 @@ Overflow The contract above shows an overflow check example. The SMTChecker does not check underflow and overflow by default for Solidity >=0.8.7, -so we need to use the command line option ``--model-checker-targets "underflow,overflow"`` +so we need to use the command-line option ``--model-checker-targets "underflow,overflow"`` or the JSON option ``settings.modelChecker.targets = ["underflow", "overflow"]``. See :ref:`this section for targets configuration`. Here, it reports the following: @@ -122,7 +122,7 @@ Here, it reports the following: If we add ``require`` statements that filter out overflow cases, the SMTChecker proves that no overflow is reachable (by not reporting warnings): -.. code-block:: Solidity +.. code-block:: solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.0; @@ -160,7 +160,7 @@ Since ``f`` is indeed monotonically increasing, the SMTChecker proves that our property is correct. You are encouraged to play with the property and the function definition to see what results come out! -.. code-block:: Solidity +.. code-block:: solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.0; @@ -182,7 +182,7 @@ The following code searches for the maximum element of an unrestricted array of numbers, and asserts the property that the found element must be greater or equal every element in the array. -.. code-block:: Solidity +.. code-block:: solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.0; @@ -216,7 +216,7 @@ All the properties are correctly proven safe. Feel free to change the properties and/or add restrictions on the array to see different results. For example, changing the code to -.. code-block:: Solidity +.. code-block:: solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.0; @@ -268,7 +268,7 @@ Let us place a robot at position (0, 0). The robot can only move diagonally, one and cannot move outside the grid. The robot's state machine can be represented by the smart contract below. -.. code-block:: Solidity +.. code-block:: solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.0; @@ -319,7 +319,7 @@ We can also trick the SMTChecker into giving us a path to a certain position we think might be reachable. We can add the property that (2, 4) is *not* reachable, by adding the following function. -.. code-block:: Solidity +.. code-block:: solidity function reach_2_4() public view { assert(!(x == 2 && y == 4)); @@ -368,7 +368,7 @@ In some cases, it is possible to automatically infer properties over state variables that are still true even if the externally called code can do anything, including reenter the caller contract. -.. code-block:: Solidity +.. code-block:: solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.0; @@ -412,7 +412,7 @@ is already "locked", so it would not be possible to change the value of ``x``, regardless of what the unknown called code does. If we "forget" to use the ``mutex`` modifier on function ``set``, the -SMTChecker is able to synthesize the behaviour of the externally called code so +SMTChecker is able to synthesize the behavior of the externally called code so that the assertion fails: .. code-block:: text @@ -483,6 +483,14 @@ All targets are checked by default, except underflow and overflow for Solidity > There is no precise heuristic on how and when to split verification targets, but it can be useful especially when dealing with large contracts. +Proved Targets +============== + +If there are any proved targets, the SMTChecker issues one warning per engine stating +how many targets were proved. If the user wishes to see all the specific +proved targets, the CLI option ``--model-checker-show-proved-safe`` and +the JSON option ``settings.modelChecker.showProvedSafe = true`` can be used. + Unproved Targets ================ @@ -491,6 +499,23 @@ how many unproved targets there are. If the user wishes to see all the specific unproved targets, the CLI option ``--model-checker-show-unproved`` and the JSON option ``settings.modelChecker.showUnproved = true`` can be used. +Unsupported Language Features +============================= + +Certain Solidity language features are not completely supported by the SMT +encoding that the SMTChecker applies, for example assembly blocks. +The unsupported construct is abstracted via overapproximation to preserve +soundness, meaning any properties reported safe are safe even though this +feature is unsupported. +However such abstraction may cause false positives when the target properties +depend on the precise behavior of the unsupported feature. +If the encoder encounters such cases it will by default report a generic warning +stating how many unsupported features it has seen. +If the user wishes to see all the specific unsupported features, the CLI option +``--model-checker-show-unsupported`` and the JSON option +``settings.modelChecker.showUnsupported = true`` can be used, where their default +value is ``false``. + Verified Contracts ================== @@ -518,13 +543,196 @@ which has the following form: "source2.sol": ["contract2", "contract3"] } +Trusted External Calls +====================== + +By default, the SMTChecker does not assume that compile-time available code +is the same as the runtime code for external calls. Take the following contracts +as an example: + +.. code-block:: solidity + + // SPDX-License-Identifier: GPL-3.0 + pragma solidity >=0.8.0; + + contract Ext { + uint public x; + function setX(uint _x) public { x = _x; } + } + contract MyContract { + function callExt(Ext _e) public { + _e.setX(42); + assert(_e.x() == 42); + } + } + +When ``MyContract.callExt`` is called, an address is given as the argument. +At deployment time, we cannot know for sure that address ``_e`` actually +contains a deployment of contract ``Ext``. +Therefore, the SMTChecker will warn that the assertion above can be violated, +which is true, if ``_e`` contains another contract than ``Ext``. + +However, it can be useful to treat these external calls as trusted, for example, +to test that different implementations of an interface conform to the same property. +This means assuming that address ``_e`` indeed was deployed as contract ``Ext``. +This mode can be enabled via the CLI option ``--model-checker-ext-calls=trusted`` +or the JSON field ``settings.modelChecker.extCalls: "trusted"``. + +Please be aware that enabling this mode can make the SMTChecker analysis much more +computationally costly. + +An important part of this mode is that it is applied to contract types and high +level external calls to contracts, and not low level calls such as ``call`` and +``delegatecall``. The storage of an address is stored per contract type, and +the SMTChecker assumes that an externally called contract has the type of the +caller expression. Therefore, casting an ``address`` or a contract to +different contract types will yield different storage values and can give +unsound results if the assumptions are inconsistent, such as the example below: + +.. code-block:: solidity + + // SPDX-License-Identifier: GPL-3.0 + pragma solidity >=0.8.0; + + contract D { + constructor(uint _x) { x = _x; } + uint public x; + function setX(uint _x) public { x = _x; } + } + + contract E { + constructor() { x = 2; } + uint public x; + function setX(uint _x) public { x = _x; } + } + + contract C { + function f() public { + address d = address(new D(42)); + + // `d` was deployed as `D`, so its `x` should be 42 now. + assert(D(d).x() == 42); // should hold + assert(D(d).x() == 43); // should fail + + // E and D have the same interface, so the following + // call would also work at runtime. + // However, the change to `E(d)` is not reflected in `D(d)`. + E(d).setX(1024); + + // Reading from `D(d)` now will show old values. + // The assertion below should fail at runtime, + // but succeeds in this mode's analysis (unsound). + assert(D(d).x() == 42); + // The assertion below should succeed at runtime, + // but fails in this mode's analysis (false positive). + assert(D(d).x() == 1024); + } + } + +Due to the above, make sure that the trusted external calls to a certain +variable of ``address`` or ``contract`` type always have the same caller +expression type. + +It is also helpful to cast the called contract's variable as the type of the +most derived type in case of inheritance. + +.. code-block:: solidity + + // SPDX-License-Identifier: GPL-3.0 + pragma solidity >=0.8.0; + + interface Token { + function balanceOf(address _a) external view returns (uint); + function transfer(address _to, uint _amt) external; + } + + contract TokenCorrect is Token { + mapping (address => uint) balance; + constructor(address _a, uint _b) { + balance[_a] = _b; + } + function balanceOf(address _a) public view override returns (uint) { + return balance[_a]; + } + function transfer(address _to, uint _amt) public override { + require(balance[msg.sender] >= _amt); + balance[msg.sender] -= _amt; + balance[_to] += _amt; + } + } + + contract Test { + function property_transfer(address _token, address _to, uint _amt) public { + require(_to != address(this)); + + TokenCorrect t = TokenCorrect(_token); + + uint xPre = t.balanceOf(address(this)); + require(xPre >= _amt); + uint yPre = t.balanceOf(_to); + + t.transfer(_to, _amt); + uint xPost = t.balanceOf(address(this)); + uint yPost = t.balanceOf(_to); + + assert(xPost == xPre - _amt); + assert(yPost == yPre + _amt); + } + } + +Note that in function ``property_transfer``, the external calls are +performed on variable ``t``. + +Another caveat of this mode are calls to state variables of contract type +outside the analyzed contract. In the code below, even though ``B`` deploys +``A``, it is also possible for the address stored in ``B.a`` to be called by +anyone outside of ``B`` in between transactions to ``B`` itself. To reflect the +possible changes to ``B.a``, the encoding allows an unbounded number of calls +to be made to ``B.a`` externally. The encoding will keep track of ``B.a``'s +storage, therefore assertion (2) should hold. However, currently the encoding +allows such calls to be made from ``B`` conceptually, therefore assertion (3) +fails. Making the encoding stronger logically is an extension of the trusted +mode and is under development. Note that the encoding does not keep track of +storage for ``address`` variables, therefore if ``B.a`` had type ``address`` +the encoding would assume that its storage does not change in between +transactions to ``B``. + +.. code-block:: solidity + + // SPDX-License-Identifier: GPL-3.0 + pragma solidity >=0.8.0; + + contract A { + uint public x; + address immutable public owner; + constructor() { + owner = msg.sender; + } + function setX(uint _x) public { + require(msg.sender == owner); + x = _x; + } + } + + contract B { + A a; + constructor() { + a = new A(); + assert(a.x() == 0); // (1) should hold + } + function g() public view { + assert(a.owner() == address(this)); // (2) should hold + assert(a.x() == 0); // (3) should hold, but fails due to a false positive + } + } + Reported Inferred Inductive Invariants ====================================== For properties that were proved safe with the CHC engine, the SMTChecker can retrieve inductive invariants that were inferred by the Horn solver as part of the proof. -Currently two types of invariants can be reported to the user: +Currently only two types of invariants can be reported to the user: - Contract Invariants: these are properties over the contract's state variables that are true before and after every possible transaction that the contract may ever run. For example, ``x >= y``, where ``x`` and ``y`` are a contract's state variables. @@ -543,7 +751,7 @@ and modulo operations inside Horn rules. Because of that, by default the Solidity division and modulo operations are encoded using the constraint ``a = b * d + m`` where ``d = a / b`` and ``m = a % b``. However, other solvers, such as Eldarica, prefer the syntactically precise operations. -The command line flag ``--model-checker-div-mod-no-slacks`` and the JSON option +The command-line flag ``--model-checker-div-mod-no-slacks`` and the JSON option ``settings.modelChecker.divModNoSlacks`` can be used to toggle the encoding depending on the used solver preferences. @@ -614,10 +822,10 @@ which is primarily an SMT solver and makes `Spacer `_ which does both. The user can choose which solvers should be used, if available, via the CLI -option ``--model-checker-solvers {all,cvc4,eld,smtlib2,z3}`` or the JSON option +option ``--model-checker-solvers {all,cvc5,eld,smtlib2,z3}`` or the JSON option ``settings.modelChecker.solvers=[smtlib2,z3]``, where: -- ``cvc4`` is only available if the ``solc`` binary is compiled with it. Only BMC uses ``cvc4``. +- ``cvc5`` is used via its binary which must be installed in the system. Only BMC uses ``cvc5``. - ``eld`` is used via its binary which must be installed in the system. Only CHC uses ``eld``, and only if ``z3`` is not enabled. - ``smtlib2`` outputs SMT/Horn queries in the `smtlib2 `_ format. These can be used together with the compiler's `callback mechanism `_ so that @@ -627,12 +835,13 @@ option ``--model-checker-solvers {all,cvc4,eld,smtlib2,z3}`` or the JSON option - if ``solc`` is compiled with it; - if a dynamic ``z3`` library of version >=4.8.x is installed in a Linux system (from Solidity 0.7.6); - - statically in ``soljson.js`` (from Solidity 0.6.9), that is, the Javascript binary of the compiler. + - statically in ``soljson.js`` (from Solidity 0.6.9), that is, the JavaScript binary of the compiler. .. note:: z3 version 4.8.16 broke ABI compatibility with previous versions and cannot be used with solc <=0.8.13. If you are using z3 >=4.8.16 please use solc - >=0.8.14. + >=0.8.14, and conversely, only use older z3 with older solc releases. + We also recommend using the latest z3 release which is what SMTChecker also does. Since both BMC and CHC use ``z3``, and ``z3`` is available in a greater variety of environments, including in the browser, most users will almost never need to be @@ -640,7 +849,7 @@ concerned about this option. More advanced users might apply this option to try alternative solvers on more complex problems. Please note that certain combinations of chosen engine and solver will lead to -the SMTChecker doing nothing, for example choosing CHC and ``cvc4``. +the SMTChecker doing nothing, for example choosing CHC and ``cvc5``. ******************************* Abstraction and False Positives @@ -735,7 +944,7 @@ the arguments. |complex) | | +-----------------------------------+--------------------------------------+ |external functions without |BMC: Erase state knowledge and assume | -|implementation |result is nondeterminisc. | +|implementation |result is nondeterministic. | | |CHC: Nondeterministic summary. | | |Try to infer invariants that hold | | |after the call returns. | diff --git a/docs/solidity_logo.svg b/docs/solidity_logo.svg new file mode 100644 index 0000000000..86b9f4995b --- /dev/null +++ b/docs/solidity_logo.svg @@ -0,0 +1,27 @@ + + + + +Vector 1 +Created with Sketch. + + + + + + + + + + + + + diff --git a/docs/structure-of-a-contract.rst b/docs/structure-of-a-contract.rst index 740cce861a..eebef172f6 100644 --- a/docs/structure-of-a-contract.rst +++ b/docs/structure-of-a-contract.rst @@ -21,8 +21,10 @@ which serves to provide a quick overview. State Variables =============== -State variables are variables whose values are permanently stored in contract -storage. +State variables are variables whose values are either permanently stored in contract +storage or, alternatively, temporarily stored in transient storage which is cleaned at +the end of each transaction. +See :ref:`data locations ` for more details. .. code-block:: solidity @@ -112,11 +114,11 @@ Events are convenience interfaces with the EVM logging facilities. .. code-block:: solidity // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.21 <0.9.0; + pragma solidity ^0.8.22; - contract SimpleAuction { - event HighestBidIncreased(address bidder, uint amount); // Event + event HighestBidIncreased(address bidder, uint amount); // Event + contract SimpleAuction { function bid() public payable { // ... emit HighestBidIncreased(msg.sender, msg.value); // Triggering event diff --git a/docs/style-guide.rst b/docs/style-guide.rst index 08050c7511..87e604124b 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -16,19 +16,19 @@ Many projects will implement their own style guides. In the event of conflicts, project specific style guides take precedence. The structure and many of the recommendations within this style guide were -taken from python's -`pep8 style guide `_. +taken from Python's +`pep8 style guide `_. The goal of this guide is *not* to be the right way or the best way to write -Solidity code. The goal of this guide is *consistency*. A quote from python's -`pep8 `_ +Solidity code. The goal of this guide is *consistency*. A quote from Python's +`pep8 `_ captures this concept well. .. note:: A style guide is about consistency. Consistency with this style guide is important. Consistency within a project is more important. Consistency within one module or function is most important. - But most importantly: **know when to be inconsistent** -- sometimes the style guide just doesn't apply. When in doubt, use your best judgment. Look at other examples and decide what looks best. And don't hesitate to ask! + But most importantly: **know when to be inconsistent** -- sometimes the style guide just doesn't apply. When in doubt, use your best judgment. Look at other examples and decide what looks best. And do not hesitate to ask! *********** @@ -233,7 +233,7 @@ Yes: bytes32[] options ); - LongAndLotsOfArgs( + emit LongAndLotsOfArgs( sender, recipient, publicKey, @@ -251,7 +251,7 @@ No: uint256 amount, bytes32[] options); - LongAndLotsOfArgs(sender, + emit LongAndLotsOfArgs(sender, recipient, publicKey, amount, @@ -448,7 +448,7 @@ No: y = 2; longVariable = 3; -Don't include a whitespace in the receive and fallback functions: +Do not include a whitespace in the receive and fallback functions: Yes: @@ -662,10 +662,11 @@ Yes: return balanceOf[from]; } - function shutdown() public onlyOwner { - selfdestruct(owner); + function increment(uint x) public pure onlyOwner returns (uint) { + return x + 1; } + No: .. code-block:: solidity @@ -674,8 +675,8 @@ No: return balanceOf[from]; } - function shutdown() onlyOwner public { - selfdestruct(owner); + function increment(uint x) onlyOwner public pure returns (uint) { + return x + 1; } For long function declarations, it is recommended to drop each argument onto @@ -1045,13 +1046,15 @@ No: Order of Layout *************** -Layout contract elements in the following order: +Contract elements should be laid out in the following order: 1. Pragma statements 2. Import statements -3. Interfaces -4. Libraries -5. Contracts +3. Events +4. Errors +5. Interfaces +6. Libraries +7. Contracts Inside each contract, library or interface, use the following order: @@ -1284,6 +1287,21 @@ Avoiding Naming Collisions This convention is suggested when the desired name collides with that of an existing state variable, function, built-in or otherwise reserved name. +Underscore Prefix for Non-external Functions and Variables +========================================================== + +* ``_singleLeadingUnderscore`` + +This convention is suggested for non-external functions and state variables (``private`` or ``internal``). State variables without a specified visibility are ``internal`` by default. + +When designing a smart contract, the public-facing API (functions that can be called by any account) +is an important consideration. +Leading underscores allow you to immediately recognize the intent of such functions, +but more importantly, if you change a function from non-external to external (including ``public``) +and rename it accordingly, this forces you to review every call site while renaming. +This can be an important manual check against unintended external functions +and a common source of security vulnerabilities (avoid find-replace-all tooling for this change). + .. _style_guide_natspec: ******* diff --git a/docs/types/conversion.rst b/docs/types/conversion.rst index e6c68df412..92f0e19dc3 100644 --- a/docs/types/conversion.rst +++ b/docs/types/conversion.rst @@ -44,7 +44,7 @@ Explicit Conversions If the compiler does not allow implicit conversion but you are confident a conversion will work, an explicit type conversion is sometimes possible. This may -result in unexpected behaviour and allows you to bypass some security +result in unexpected behavior and allows you to bypass some security features of the compiler, so be sure to test that the result is what you want and expect! @@ -130,6 +130,7 @@ If the array is shorter than the target type, it will be padded with zeros at th } } +.. index:: ! literal;conversion, literal;rational, literal;hexadecimal number .. _types-conversion-literals: Conversions between Literals and Elementary Types @@ -152,6 +153,8 @@ that is large enough to represent it without truncation: converted to an integer type. From 0.8.0, such explicit conversions are as strict as implicit conversions, i.e., they are only allowed if the literal fits in the resulting range. +.. index:: literal;string, literal;hexadecimal + Fixed-Size Byte Arrays ---------------------- @@ -171,17 +174,18 @@ converted to any fixed-size bytes type: bytes4 g = 0x0; // fine String literals and hex string literals can be implicitly converted to fixed-size byte arrays, -if their number of characters matches the size of the bytes type: +if their number of characters is less than or equal to the size of the bytes type: .. code-block:: solidity bytes2 a = hex"1234"; // fine bytes2 b = "xy"; // fine - bytes2 c = hex"12"; // not allowed - bytes2 d = hex"123"; // not allowed - bytes2 e = "x"; // not allowed + bytes2 c = hex"12"; // fine + bytes2 e = "x"; // fine bytes2 f = "xyz"; // not allowed +.. index:: literal;address + Addresses --------- @@ -194,4 +198,4 @@ An ``address a`` can be converted explicitly to ``address payable`` via ``payabl .. note:: Prior to version 0.8.0, it was possible to explicitly convert from any integer type (of any size, signed or unsigned) to ``address`` or ``address payable``. - Starting with in 0.8.0 only conversion from ``uint160`` is allowed. \ No newline at end of file + Starting with 0.8.0 only conversion from ``uint160`` is allowed. diff --git a/docs/types/mapping-types.rst b/docs/types/mapping-types.rst index c59d6e8454..454caaf05d 100644 --- a/docs/types/mapping-types.rst +++ b/docs/types/mapping-types.rst @@ -4,12 +4,13 @@ Mapping Types ============= -Mapping types use the syntax ``mapping(KeyType => ValueType)`` and variables -of mapping type are declared using the syntax ``mapping(KeyType => ValueType) VariableName``. -The ``KeyType`` can be any -built-in value type, ``bytes``, ``string``, or any contract or enum type. Other user-defined -or complex types, such as mappings, structs or array types are not allowed. -``ValueType`` can be any type, including mappings, arrays and structs. +Mapping types use the syntax ``mapping(KeyType KeyName? => ValueType ValueName?)`` and variables of +mapping type are declared using the syntax ``mapping(KeyType KeyName? => ValueType ValueName?) +VariableName``. The ``KeyType`` can be any built-in value type, ``bytes``, ``string``, or any +contract or enum type. Other user-defined or complex types, such as mappings, structs or array types +are not allowed. ``ValueType`` can be any type, including mappings, arrays and structs. ``KeyName`` +and ``ValueName`` are optional (so ``mapping(KeyType => ValueType)`` works as well) and can be any +valid identifier that is not a type. You can think of mappings as `hash tables `_, which are virtually initialised such that every possible key exists and is mapped to a value whose @@ -29,8 +30,10 @@ of contract functions that are publicly visible. These restrictions are also true for arrays and structs that contain mappings. You can mark state variables of mapping type as ``public`` and Solidity creates a -:ref:`getter ` for you. The ``KeyType`` becomes a parameter for the getter. -If ``ValueType`` is a value type or a struct, the getter returns ``ValueType``. +:ref:`getter ` for you. The ``KeyType`` becomes a parameter +with name ``KeyName`` (if specified) for the getter. +If ``ValueType`` is a value type or a struct, the getter returns ``ValueType`` with +name ``ValueName`` (if specified). If ``ValueType`` is an array or a mapping, the getter has one parameter for each ``KeyType``, recursively. @@ -64,6 +67,25 @@ contract that returns the value at the specified address. The example below is a simplified version of an `ERC20 token `_. ``_allowances`` is an example of a mapping type inside another mapping type. + +In the example below, the optional ``KeyName`` and ``ValueName`` are provided for the mapping. +It does not affect any contract functionality or bytecode, it only sets the ``name`` field +for the inputs and outputs in the ABI for the mapping's getter. + +.. code-block:: solidity + + // SPDX-License-Identifier: GPL-3.0 + pragma solidity ^0.8.18; + + contract MappingExampleWithNames { + mapping(address user => uint balance) public balances; + + function update(uint newBalance) public { + balances[msg.sender] = newBalance; + } + } + + The example below uses ``_allowances`` to record the amount someone else is allowed to withdraw from your account. .. code-block:: solidity @@ -73,8 +95,8 @@ The example below uses ``_allowances`` to record the amount someone else is allo contract MappingExample { - mapping (address => uint256) private _balances; - mapping (address => mapping (address => uint256)) private _allowances; + mapping(address => uint256) private _balances; + mapping(address => mapping(address => uint256)) private _allowances; event Transfer(address indexed from, address indexed to, uint256 value); event Approval(address indexed owner, address indexed spender, uint256 value); diff --git a/docs/types/reference-types.rst b/docs/types/reference-types.rst index 6a7667b6b9..4a053d51be 100644 --- a/docs/types/reference-types.rst +++ b/docs/types/reference-types.rst @@ -28,6 +28,9 @@ annotation, the "data location", about where it is stored. There are three data ``memory``, ``storage`` and ``calldata``. Calldata is a non-modifiable, non-persistent area where function arguments are stored, and behaves mostly like memory. +.. note:: + ``transient`` is not yet supported as a data location for reference types. + .. note:: If you can, try to use ``calldata`` as data location because it will avoid copies and also makes sure that the data cannot be modified. Arrays and structs with ``calldata`` @@ -47,8 +50,8 @@ non-persistent area where function arguments are stored, and behaves mostly like .. _data-location-assignment: -Data location and assignment behaviour -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Data location and assignment behavior +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Data locations are not only relevant for persistency of data, but also for the semantics of assignments: @@ -235,7 +238,7 @@ with the :ref:`default value`. } } -.. index:: ! array;literals, ! inline;arrays +.. index:: ! literal;array, ! inline;arrays Array Literals ^^^^^^^^^^^^^^ @@ -372,7 +375,7 @@ Array Members .. note:: In EVM versions before Byzantium, it was not possible to access - dynamic arrays return from function calls. If you call functions + dynamic arrays returned from function calls. If you call functions that return dynamic arrays, make sure to use an EVM that is set to Byzantium mode. @@ -392,8 +395,7 @@ Array Members // Data location for all state variables is storage. bool[2][] pairsOfFlags; - // newPairs is stored in memory - the only possibility - // for public contract function arguments + // newPairs is stored in memory function setAllFlagPairs(bool[2][] memory newPairs) public { // assignment to a storage array performs a copy of ``newPairs`` and // replaces the complete array ``pairsOfFlags``. @@ -582,10 +584,10 @@ and the assignment will effectively garble the length of ``x``. To be safe, only enlarge bytes arrays by at most one element during a single assignment and do not simultaneously index-access the array in the same statement. -While the above describes the behaviour of dangling storage references in the +While the above describes the behavior of dangling storage references in the current version of the compiler, any code with dangling references should be -considered to have *undefined behaviour*. In particular, this means that -any future version of the compiler may change the behaviour of code that +considered to have *undefined behavior*. In particular, this means that +any future version of the compiler may change the behavior of code that involves dangling references. Be sure to avoid dangling references in your code! @@ -641,7 +643,7 @@ Array slices are useful to ABI-decode secondary data passed in function paramete /// after doing basic validation on the address argument. function forward(bytes calldata payload) external { bytes4 sig = bytes4(payload[:4]); - // Due to truncating behaviour, bytes4(payload) performs identically. + // Due to truncating behavior, bytes4(payload) performs identically. // bytes4 sig = bytes4(payload); if (sig == bytes4(keccak256("setOwner(address)"))) { address owner = abi.decode(payload[4:], (address)); @@ -686,11 +688,11 @@ shown in the following example: uint fundingGoal; uint numFunders; uint amount; - mapping (uint => Funder) funders; + mapping(uint => Funder) funders; } uint numCampaigns; - mapping (uint => Campaign) campaigns; + mapping(uint => Campaign) campaigns; function newCampaign(address payable beneficiary, uint goal) public returns (uint campaignID) { campaignID = numCampaigns++; // campaignID is return variable diff --git a/docs/types/value-types.rst b/docs/types/value-types.rst index de2724afa9..5efb080554 100644 --- a/docs/types/value-types.rst +++ b/docs/types/value-types.rst @@ -4,10 +4,15 @@ Value Types =========== -The following types are also called value types because variables of these -types will always be passed by value, i.e. they are always copied when they +The following are called value types because their variables will always be passed by value, i.e. they are always copied when they are used as function arguments or in assignments. +Unlike :ref:`reference types `, value type declarations do not +specify a data location since they are small enough to be stored on the stack. +The only exception are :ref:`state variables `. +Those are by default located in storage, but can also be marked as +:ref:`transient `, :ref:`constant or immutable `. + .. index:: ! bool, ! true, ! false Booleans @@ -47,7 +52,7 @@ access the minimum and maximum value representable by the type. Integers in Solidity are restricted to a certain range. For example, with ``uint32``, this is ``0`` up to ``2**32 - 1``. There are two modes in which arithmetic is performed on these types: The "wrapping" or "unchecked" mode and the "checked" mode. - By default, arithmetic is always "checked", which mean that if the result of an operation falls outside the value range + By default, arithmetic is always "checked", meaning that if an operation's result falls outside the value range of the type, the call is reverted through a :ref:`failing assertion`. You can switch to "unchecked" mode using ``unchecked { ... }``. More details can be found in the section about :ref:`unchecked `. @@ -141,7 +146,7 @@ Exponentiation Exponentiation is only available for unsigned types in the exponent. The resulting type of an exponentiation is always equal to the type of the base. Please take care that it is -large enough to hold the result and prepare for potential assertion failures or wrapping behaviour. +large enough to hold the result and prepare for potential assertion failures or wrapping behavior. .. note:: In checked mode, exponentiation only uses the comparatively cheap ``exp`` opcode for small bases. @@ -182,7 +187,7 @@ Operators: Address ------- -The address type comes in two flavours, which are largely identical: +The address type comes in two largely identical flavors: - ``address``: Holds a 20 byte value (size of an Ethereum address). - ``address payable``: Same as ``address``, but with the additional members ``transfer`` and ``send``. @@ -258,13 +263,13 @@ reverts on failure. * ``send`` -Send is the low-level counterpart of ``transfer``. If the execution fails, the current contract will not stop with an exception, but ``send`` will return ``false``. +``send`` is the low-level counterpart of ``transfer``. If the execution fails, the current contract will not stop with an exception, but ``send`` will return ``false``. .. warning:: There are some dangers in using ``send``: The transfer fails if the call stack depth is at 1024 (this can always be forced by the caller) and it also fails if the recipient runs out of gas. So in order to make safe Ether transfers, always check the return value of ``send``, use ``transfer`` or even better: - use a pattern where the recipient withdraws the money. + use a pattern where the recipient withdraws the Ether. * ``call``, ``delegatecall`` and ``staticcall`` @@ -339,6 +344,13 @@ You can query the deployed code for any smart contract. Use ``.code`` to get the ``bytes memory``, which might be empty. Use ``.codehash`` to get the Keccak-256 hash of that code (as a ``bytes32``). Note that ``addr.codehash`` is cheaper than using ``keccak256(addr.code)``. +.. warning:: + The output of ``addr.codehash`` may be ``0`` if the account associated with ``addr`` is empty or non-existent + (i.e., it has no code, zero balance, and zero nonce as defined by `EIP-161 `_). + If the account has no code but a non-zero balance or nonce, then ``addr.codehash`` will output the Keccak-256 hash of empty data + (i.e., ``keccak256("")`` which is equal to ``c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470``), as defined by + `EIP-1052 `_. + .. note:: All contracts can be converted to ``address`` type, so it is possible to query the balance of the current contract using ``address(this).balance``. @@ -416,15 +428,7 @@ Members: .. note:: Prior to version 0.8.0, ``byte`` used to be an alias for ``bytes1``. -Dynamically-sized byte array ----------------------------- - -``bytes``: - Dynamically-sized byte array, see :ref:`arrays`. Not a value-type! -``string``: - Dynamically-sized UTF-8-encoded string, see :ref:`arrays`. Not a value-type! - -.. index:: address, literal;address +.. index:: address, ! literal;address .. _address_literals: @@ -440,7 +444,7 @@ an error. You can prepend (for integer types) or append (for bytesNN types) zero .. note:: The mixed-case address checksum format is defined in `EIP-55 `_. -.. index:: literal, literal;rational +.. index:: integer, rational number, ! literal;rational .. _rational_literals: @@ -518,7 +522,7 @@ regardless of the type of the right (exponent) operand. uint128 a = 1; uint128 b = 2.5 + a + 0.5; -.. index:: literal, literal;string, string +.. index:: ! literal;string, string .. _string_literals: String Literals and Types @@ -565,6 +569,8 @@ character sequence ``abcdef``. Any Unicode line terminator which is not a newline (i.e. LF, VF, FF, CR, NEL, LS, PS) is considered to terminate the string literal. Newline only terminates the string literal if it is not preceded by a ``\``. +.. index:: ! literal;unicode + Unicode Literals ---------------- @@ -575,7 +581,7 @@ They also support the very same escape sequences as regular string literals. string memory a = unicode"Hello 😃"; -.. index:: literal, bytes +.. index:: ! literal;hexadecimal, bytes Hexadecimal Literals -------------------- @@ -589,7 +595,8 @@ of the hexadecimal sequence. Multiple hexadecimal literals separated by whitespace are concatenated into a single literal: ``hex"00112233" hex"44556677"`` is equivalent to ``hex"0011223344556677"`` -Hexadecimal literals behave like :ref:`string literals ` and have the same convertibility restrictions. +Hexadecimal literals in some ways behave like :ref:`string literals ` but are not +implicitly convertible to the ``string`` type. .. index:: enum @@ -653,13 +660,13 @@ smallest and respectively largest value of the given enum. .. _user-defined-value-types: -User Defined Value Types +User-defined Value Types ------------------------ -A user defined value type allows creating a zero cost abstraction over an elementary value type. +A user-defined value type allows creating a zero cost abstraction over an elementary value type. This is similar to an alias, but with stricter type requirements. -A user defined value type is defined using ``type C is V``, where ``C`` is the name of the newly +A user-defined value type is defined using ``type C is V``, where ``C`` is the name of the newly introduced type and ``V`` has to be a built-in value type (the "underlying type"). The function ``C.wrap`` is used to convert from the underlying type to the custom type. Similarly, the function ``C.unwrap`` is used to convert from the custom type to the underlying type. @@ -680,7 +687,7 @@ type with 18 decimals and a minimal library to do arithmetic operations on the t // SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.8; - // Represent a 18 decimal, 256 bit wide fixed point type using a user defined value type. + // Represent a 18 decimal, 256 bit wide fixed point type using a user-defined value type. type UFixed256x18 is uint256; /// A minimal library to do fixed point operations on UFixed256x18. @@ -811,6 +818,12 @@ functions. Marking the parameters as ``calldata`` only affects the implementation of the external function and is meaningless in a function pointer on the caller's side. +.. warning:: + Comparison of internal function pointers can have unexpected results in the legacy pipeline with the optimizer enabled, + as it can collapse identical functions into one, which will then lead to said function pointers comparing as equal instead of not. + Such comparisons are not advised, and will lead to the compiler issuing a warning, until the next breaking release (0.9.0), + when the warning will be upgraded to an error, thereby making such comparisons disallowed. + Libraries are excluded because they require a ``delegatecall`` and use :ref:`a different ABI convention for their selectors `. Functions declared in interfaces do not have definitions so pointing at them does not make sense either. diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index cf4fb2bd7c..fce4cf96d9 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -1,8 +1,10 @@ +.. index:: ! denomination + ************************************** Units and Globally Available Variables ************************************** -.. index:: wei, finney, szabo, gwei, ether +.. index:: ! wei, ! finney, ! szabo, ! gwei, ! ether, ! denomination;ether Ether Units =========== @@ -21,7 +23,7 @@ The only effect of the subdenomination suffix is a multiplication by a power of .. note:: The denominations ``finney`` and ``szabo`` have been removed in version 0.7.0. -.. index:: time, seconds, minutes, hours, days, weeks, years +.. index:: ! seconds, ! minutes, ! hours, ! days, ! weeks, ! years, ! denomination;time Time Units ========== @@ -52,7 +54,7 @@ interpret a function parameter in days, you can in the following way: function f(uint start, uint daysAfter) public { if (block.timestamp >= start + daysAfter * 1 days) { - // ... + // ... } } @@ -65,19 +67,25 @@ There are special variables and functions which always exist in the global namespace and are mainly used to provide information about the blockchain or are general-use utility functions. -.. index:: abi, block, coinbase, difficulty, encode, number, block;number, timestamp, block;timestamp, msg, data, gas, sender, value, gas price, origin +.. index:: abi, block, coinbase, difficulty, prevrandao, encode, number, block;number, timestamp, block;timestamp, block;basefee, block;blobbasefee, msg, data, gas, sender, value, gas price, origin Block and Transaction Properties -------------------------------- - ``blockhash(uint blockNumber) returns (bytes32)``: hash of the given block when ``blocknumber`` is one of the 256 most recent blocks; otherwise returns zero +- ``blobhash(uint index) returns (bytes32)``: versioned hash of the ``index``-th blob associated with the current transaction. + A versioned hash consists of a single byte representing the version (currently ``0x01``), followed by the last 31 bytes + of the SHA256 hash of the KZG commitment (`EIP-4844 `_). + Returns zero if no blob with the given index exists. - ``block.basefee`` (``uint``): current block's base fee (`EIP-3198 `_ and `EIP-1559 `_) +- ``block.blobbasefee`` (``uint``): current block's blob base fee (`EIP-7516 `_ and `EIP-4844 `_) - ``block.chainid`` (``uint``): current chain id - ``block.coinbase`` (``address payable``): current block miner's address -- ``block.difficulty`` (``uint``): current block difficulty +- ``block.difficulty`` (``uint``): current block difficulty (``EVM < Paris``). For other EVM versions it behaves as a deprecated alias for ``block.prevrandao`` (`EIP-4399 `_ ) - ``block.gaslimit`` (``uint``): current block gaslimit - ``block.number`` (``uint``): current block number +- ``block.prevrandao`` (``uint``): random number provided by the beacon chain (``EVM >= Paris``) - ``block.timestamp`` (``uint``): current block timestamp as seconds since unix epoch - ``gasleft() returns (uint256)``: remaining gas - ``msg.data`` (``bytes calldata``): complete calldata @@ -104,7 +112,7 @@ Block and Transaction Properties Both the timestamp and the block hash can be influenced by miners to some degree. Bad actors in the mining community can for example run a casino payout function on a chosen hash - and just retry a different hash if they did not receive any money. + and just retry a different hash if they did not receive any compensation, e.g. Ether. The current block timestamp must be strictly larger than the timestamp of the last block, but the only guarantee is that it will be somewhere between the timestamps of two @@ -232,7 +240,7 @@ Mathematical and Cryptographic Functions the ecrecover function remained unchanged. This is usually not a problem unless you require signatures to be unique or use them to identify items. - OpenZeppelin have a `ECDSA helper library `_ that you can use as a wrapper for ``ecrecover`` without this issue. + OpenZeppelin has an `ECDSA helper library `_ that you can use as a wrapper for ``ecrecover`` without this issue. .. note:: @@ -279,7 +287,7 @@ For more information, see the section on :ref:`address`. There are some dangers in using ``send``: The transfer fails if the call stack depth is at 1024 (this can always be forced by the caller) and it also fails if the recipient runs out of gas. So in order to make safe Ether transfers, always check the return value of ``send``, use ``transfer`` or even better: - Use a pattern where the recipient withdraws the money. + Use a pattern where the recipient withdraws the Ether. .. warning:: Due to the fact that the EVM considers a call to a non-existing contract to always succeed, @@ -310,13 +318,16 @@ For more information, see the section on :ref:`address`. semantics than ``delegatecall``. -.. index:: this, selfdestruct +.. index:: this, selfdestruct, super -Contract Related +Contract-related ---------------- ``this`` (current contract's type) - the current contract, explicitly convertible to :ref:`address` + The current contract, explicitly convertible to :ref:`address` + +``super`` + A contract one level higher in the inheritance hierarchy ``selfdestruct(address payable recipient)`` Destroy the current contract, sending its funds to the given :ref:`address` @@ -326,11 +337,27 @@ Contract Related - the receiving contract's receive function is not executed. - the contract is only really destroyed at the end of the transaction and ``revert`` s might "undo" the destruction. - - - Furthermore, all functions of the current contract are callable directly including the current function. +.. warning:: + From ``EVM >= Cancun`` onwards, ``selfdestruct`` will **only** send all Ether in the account to the given recipient and not destroy the contract. + However, when ``selfdestruct`` is called in the same transaction that creates the contract calling it, + the behaviour of ``selfdestruct`` before Cancun hardfork (i.e., ``EVM <= Shanghai``) is preserved and will destroy the current contract, + deleting any data, including storage keys, code and the account itself. + See `EIP-6780 `_ for more details. + + The new behaviour is the result of a network-wide change that affects all contracts present on + the Ethereum mainnet and testnets. + It is important to note that this change is dependent on the EVM version of the chain on which + the contract is deployed. + The ``--evm-version`` setting used when compiling the contract has no bearing on it. + + Also, note that the ``selfdestruct`` opcode has been deprecated in Solidity version 0.8.18, + as recommended by `EIP-6049 `_. + The deprecation is still in effect and the compiler will still emit warnings on its use. + Any use in newly deployed contracts is strongly discouraged even if the new behavior is taken into account. + Future changes to the EVM might further reduce the functionality of the opcode. + .. note:: Prior to version 0.5.0, there was a function called ``suicide`` with the same semantics as ``selfdestruct``. @@ -372,7 +399,7 @@ The following properties are available for a contract type ``C``: In addition to the properties above, the following properties are available for an interface type ``I``: -``type(I).interfaceId``: +``type(I).interfaceId`` A ``bytes4`` value containing the `EIP-165 `_ interface identifier of the given interface ``I``. This identifier is defined as the ``XOR`` of all function selectors defined within the interface itself - excluding all inherited functions. @@ -394,4 +421,4 @@ These keywords are reserved in Solidity. They might become part of the syntax in ``define``, ``final``, ``implements``, ``in``, ``inline``, ``let``, ``macro``, ``match``, ``mutable``, ``null``, ``of``, ``partial``, ``promise``, ``reference``, ``relocatable``, ``sealed``, ``sizeof``, ``static``, ``supports``, ``switch``, ``typedef``, ``typeof``, -``var``. \ No newline at end of file +``var``. diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index e5b5f00ec0..004bb4a614 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -15,7 +15,7 @@ Using the Commandline Compiler Basic Usage ----------- -One of the build targets of the Solidity repository is ``solc``, the solidity commandline compiler. +One of the build targets of the Solidity repository is ``solc``, the Solidity commandline compiler. Using ``solc --help`` provides you with an explanation of all options. The compiler can produce various outputs, ranging from simple binaries and assembly over an abstract syntax tree (parse tree) to estimations of gas usage. If you only want to compile a single file, you run it as ``solc --bin sourceFile.sol`` and it will print the binary. If you want to get some of the more advanced output variants of ``solc``, it is probably better to tell it to output everything to separate files using ``solc -o outputDirectory --bin --ast-compact-json --asm sourceFile.sol``. @@ -54,7 +54,7 @@ or ../ ` are treated as relative to the directories specified us Furthermore, the part of the path added via these options will not appear in the contract metadata. For security reasons the compiler has :ref:`restrictions on what directories it can access `. -Directories of source files specified on the command line and target paths of +Directories of source files specified on the command-line and target paths of remappings are automatically allowed to be accessed by the file reader, but everything else is rejected by default. Additional paths (and their subdirectories) can be allowed via the @@ -71,7 +71,7 @@ For a detailed explanation with examples and discussion of corner cases please r Library Linking --------------- -If your contracts use :ref:`libraries `, you will notice that the bytecode contains substrings of the form ``__$53aea86b7d70b31448b230b20ae141a537$__``. These are placeholders for the actual library addresses. +If your contracts use :ref:`libraries `, you will notice that the bytecode contains substrings of the form ``__$53aea86b7d70b31448b230b20ae141a537$__`` `(format was different `_. These are placeholders for the actual library addresses. The placeholder is a 34 character prefix of the hex encoding of the keccak256 hash of the fully qualified library name. The bytecode file will also contain lines of the form ``// -> `` at the end to help identify which libraries the placeholders represent. Note that the fully qualified library name @@ -114,15 +114,15 @@ Setting the EVM Version to Target ********************************* When you compile your contract code you can specify the Ethereum virtual machine -version to compile for to avoid particular features or behaviours. +version to compile for to avoid particular features or behaviors. .. warning:: Compiling for the wrong EVM version can result in wrong, strange and failing - behaviour. Please ensure, especially if running a private chain, that you + behavior. Please ensure, especially if running a private chain, that you use matching EVM versions. -On the command line, you can select the EVM version as follows: +On the command-line, you can select the EVM version as follows: .. code-block:: shell @@ -147,20 +147,20 @@ Target Options Below is a list of target EVM versions and the compiler-relevant changes introduced at each version. Backward compatibility is not guaranteed between each version. -- ``homestead`` +- ``homestead`` (*support deprecated*) - (oldest version) -- ``tangerineWhistle`` +- ``tangerineWhistle`` (*support deprecated*) - Gas cost for access to other accounts increased, relevant for gas estimation and the optimizer. - All gas sent by default for external calls, previously a certain amount had to be retained. -- ``spuriousDragon`` +- ``spuriousDragon`` (*support deprecated*) - Gas cost for the ``exp`` opcode increased, relevant for gas estimation and the optimizer. -- ``byzantium`` +- ``byzantium`` (*support deprecated*) - Opcodes ``returndatacopy``, ``returndatasize`` and ``staticcall`` are available in assembly. - The ``staticcall`` opcode is used when calling non-library view or pure functions, which prevents the functions from modifying state at the EVM level, i.e., even applies when you use invalid type conversions. - It is possible to access dynamic data returned from function calls. - ``revert`` opcode introduced, which means that ``revert()`` will not waste gas. - ``constantinople`` - - Opcodes ``create2`, ``extcodehash``, ``shl``, ``shr`` and ``sar`` are available in assembly. + - Opcodes ``create2``, ``extcodehash``, ``shl``, ``shr`` and ``sar`` are available in assembly. - Shifting operators use shifting opcodes and thus need less gas. - ``petersburg`` - The compiler behaves the same way as with constantinople. @@ -170,10 +170,18 @@ at each version. Backward compatibility is not guaranteed between each version. - Gas costs for ``SLOAD``, ``*CALL``, ``BALANCE``, ``EXT*`` and ``SELFDESTRUCT`` increased. The compiler assumes cold gas costs for such operations. This is relevant for gas estimation and the optimizer. -- ``london`` (**default**) +- ``london`` - The block's base fee (`EIP-3198 `_ and `EIP-1559 `_) can be accessed via the global ``block.basefee`` or ``basefee()`` in inline assembly. - ``paris`` - - No changes, however the semantics of the ``difficulty`` value have changed (see `EIP-4399 `_). + - Introduces ``prevrandao()`` and ``block.prevrandao``, and changes the semantics of the now deprecated ``block.difficulty``, disallowing ``difficulty()`` in inline assembly (see `EIP-4399 `_). +- ``shanghai`` + - Smaller code size and gas savings due to the introduction of ``push0`` (see `EIP-3855 `_). +- ``cancun`` (**default**) + - The block's blob base fee (`EIP-7516 `_ and `EIP-4844 `_) can be accessed via the global ``block.blobbasefee`` or ``blobbasefee()`` in inline assembly. + - Introduces ``blobhash()`` in inline assembly and a corresponding global function to retrieve versioned hashes of blobs associated with the transaction (see `EIP-4844 `_). + - Opcode ``mcopy`` is available in assembly (see `EIP-5656 `_). + - Opcodes ``tstore`` and ``tload`` are available in assembly (see `EIP-1153 `_). +- ``prague`` (**experimental**) .. index:: ! standard JSON, ! --standard-json .. _compiler-api: @@ -201,7 +209,7 @@ Input Description .. code-block:: javascript { - // Required: Source code language. Currently supported are "Solidity" and "Yul". + // Required: Source code language. Currently supported are "Solidity", "Yul", "SolidityAST" (experimental), "EVMAssembly" (experimental). "language": "Solidity", // Required "sources": @@ -225,16 +233,43 @@ Input Description "bzzr://56ab...", "ipfs://Qma...", "/tmp/path/to/file.sol" - // If files are used, their directories should be added to the command line via + // If files are used, their directories should be added to the command-line via // `--allow-paths `. ] }, - "destructible": + "settable": { // Optional: keccak256 hash of the source file "keccak256": "0x234...", // Required (unless "urls" is used): literal contents of the source file - "content": "contract destructible is owned { function shutdown() { if (msg.sender == owner) selfdestruct(owner); } }" + "content": "contract settable is owned { uint256 private x = 0; function set(uint256 _x) public { if (msg.sender == owner) x = _x; } }" + }, + "myFile.sol_json.ast": + { + // If language is set to "SolidityAST", an AST needs to be supplied under the "ast" key + // and there can be only one source file present. + // The format is the same as used by the `ast` output. + // Note that importing ASTs is experimental and in particular that: + // - importing invalid ASTs can produce undefined results and + // - no proper error reporting is available on invalid ASTs. + // Furthermore, note that the AST import only consumes the fields of the AST as + // produced by the compiler in "stopAfter": "parsing" mode and then re-performs + // analysis, so any analysis-based annotations of the AST are ignored upon import. + "ast": { ... } + }, + "myFile_evm.json": + { + // If language is set to "EVMAssembly", an EVM Assembly JSON object needs to be supplied + // under the "assemblyJson" key and there can be only one source file present. + // The format is the same as used by the `evm.legacyAssembly` output or `--asm-json` + // output on the command line. + // Note that importing EVM assembly is experimental. + "assemblyJson": + { + ".code": [ ... ], + ".data": { ... }, // optional + "sourceList": [ ... ] // optional (if no `source` node was defined in any `.code` object) + } } }, // Optional @@ -262,9 +297,9 @@ Input Description // The peephole optimizer is always on if no details are given, // use details to switch it off. "peephole": true, - // The inliner is always on if no details are given, - // use details to switch it off. - "inliner": true, + // The inliner is always off if no details are given, + // use details to switch it on. + "inliner": false, // The unused jumpdest remover is always on if no details are given, // use details to switch it off. "jumpdestRemover": true, @@ -277,6 +312,9 @@ Input Description "cse": false, // Optimize representation of literal numbers and strings in code. "constantOptimizer": false, + // Use unchecked arithmetic when incrementing the counter of for loops + // under certain circumstances. It is always on if no details are given. + "simpleCounterForLoopUncheckedIncrement": true, // The new Yul optimizer. Mostly operates on the code of ABI coder v2 // and inline assembly. // It is activated together with the global optimizer setting @@ -294,7 +332,7 @@ Input Description // optimization-sequence:clean-up-sequence. For more information see // "The Optimizer > Selecting Optimizations". // This field is optional, and if not provided, the default sequences for both - // optimization and clean-up are used. If only one of the options is provivded + // optimization and clean-up are used. If only one of the sequences is provided // the other will not be run. // If only the delimiter ":" is provided then neither the optimization nor the clean-up // sequence will be run. @@ -306,8 +344,9 @@ Input Description }, // Version of the EVM to compile for. // Affects type checking and code generation. Can be homestead, - // tangerineWhistle, spuriousDragon, byzantium, constantinople, petersburg, istanbul, berlin, london or paris - "evmVersion": "byzantium", + // tangerineWhistle, spuriousDragon, byzantium, constantinople, + // petersburg, istanbul, berlin, london, paris, shanghai, cancun (default) or prague. + "evmVersion": "cancun", // Optional: Change compilation pipeline to go through the Yul intermediate representation. // This is false by default. "viaIR": true, @@ -348,7 +387,7 @@ Input Description // Addresses of the libraries. If not all libraries are given here, // it can result in unlinked objects whose output data is different. "libraries": { - // The top level key is the the name of the source file where the library is used. + // The top level key is the name of the source file where the library is used. // If remappings are used, this source file should match the global path // after remappings were applied. // If this key is an empty string, that refers to a global level. @@ -365,7 +404,8 @@ Input Description // but to the whole source file like the AST. // A star as contract name refers to all contracts in the file. // Similarly, a star as a file name matches all files. - // To select all outputs the compiler can possibly generate, use + // To select all outputs the compiler can possibly generate, with the exclusion of + // Yul intermediate representation outputs, use // "outputSelection: { "*": { "*": [ "*" ], "": [ "*" ] } }" // but note that this might slow down the compilation process needlessly. // @@ -380,8 +420,11 @@ Input Description // userdoc - User documentation (natspec) // metadata - Metadata // ir - Yul intermediate representation of the code before optimization + // irAst - AST of Yul intermediate representation of the code before optimization // irOptimized - Intermediate representation after optimization - // storageLayout - Slots, offsets and types of the contract's state variables. + // irOptimizedAst - AST of intermediate representation after optimization + // storageLayout - Slots, offsets and types of the contract's state variables in storage. + // transientStorageLayout - Slots, offsets and types of the contract's state variables in transient storage. // evm.assembly - New assembly format // evm.legacyAssembly - Old-style assembly format in JSON // evm.bytecode.functionDebugData - Debugging information at function level @@ -394,10 +437,8 @@ Input Description // evm.deployedBytecode.immutableReferences - Map from AST ids to bytecode ranges that reference immutables // evm.methodIdentifiers - The list of function hashes // evm.gasEstimates - Function gas estimates - // ewasm.wast - Ewasm in WebAssembly S-expressions format - // ewasm.wasm - Ewasm in WebAssembly binary format // - // Note that using a using `evm`, `evm.bytecode`, `ewasm`, etc. will select every + // Note that using `evm`, `evm.bytecode`, etc. will select every // target part of that output. Additionally, `*` can be used as a wildcard to request everything. // "outputSelection": { @@ -433,13 +474,21 @@ Input Description "divModNoSlacks": false, // Choose which model checker engine to use: all (default), bmc, chc, none. "engine": "chc", + // Choose whether external calls should be considered trusted in case the + // code of the called function is available at compile-time. + // For details see the SMTChecker section. + "extCalls": "trusted", // Choose which types of invariants should be reported to the user: contract, reentrancy. "invariants": ["contract", "reentrancy"], + // Choose whether to output all proved targets. The default is `false`. + "showProvedSafe": true, // Choose whether to output all unproved targets. The default is `false`. "showUnproved": true, + // Choose whether to output all unsupported language features. The default is `false`. + "showUnsupported": true, // Choose which solvers should be used, if available. // See the Formal Verification section for the solvers description. - "solvers": ["cvc4", "smtlib2", "z3"], + "solvers": ["cvc5", "smtlib2", "z3"], // Choose which targets should be checked: constantCondition, // underflow, overflow, divByZero, balance, assert, popEmptyArray, outOfBounds. // If the option is not given all targets are checked by default, @@ -483,7 +532,7 @@ Output Description // Mandatory: Error type, such as "TypeError", "InternalCompilerError", "Exception", etc. // See below for complete list of types. "type": "TypeError", - // Mandatory: Component where the error originated, such as "general", "ewasm", etc. + // Mandatory: Component where the error originated, such as "general" etc. "component": "general", // Mandatory ("error", "warning" or "info", but please note that this may be extended in the future) "severity": "error", @@ -520,10 +569,18 @@ Output Description "userdoc": {}, // Developer documentation (natspec) "devdoc": {}, - // Intermediate representation (string) + // Intermediate representation before optimization (string) "ir": "", + // AST of intermediate representation before optimization + "irAst": {/* ... */}, + // Intermediate representation after optimization (string) + "irOptimized": "", + // AST of intermediate representation after optimization + "irOptimizedAst": {/* ... */}, // See the Storage Layout documentation. "storageLayout": {"storage": [/* ... */], "types": {/* ... */} }, + // See the Storage Layout documentation. + "transientStorageLayout": {"storage": [/* ... */], "types": {/* ... */} }, // EVM-related outputs "evm": { // Assembly (string) @@ -599,13 +656,6 @@ Output Description "heavyLifting()": "infinite" } } - }, - // Ewasm related outputs - "ewasm": { - // S-expressions format - "wast": "", - // Binary format (hex string) - "wasm": "" } } } @@ -628,231 +678,6 @@ Error Types 10. ``Exception``: Unknown failure during compilation - this should be reported as an issue. 11. ``CompilerError``: Invalid use of the compiler stack - this should be reported as an issue. 12. ``FatalError``: Fatal error not processed correctly - this should be reported as an issue. -13. ``YulException``: Error during Yul Code generation - this should be reported as an issue. +13. ``YulException``: Error during Yul code generation - this should be reported as an issue. 14. ``Warning``: A warning, which didn't stop the compilation, but should be addressed if possible. 15. ``Info``: Information that the compiler thinks the user might find useful, but is not dangerous and does not necessarily need to be addressed. - - -.. _compiler-tools: - -Compiler Tools -************** - -solidity-upgrade ----------------- - -``solidity-upgrade`` can help you to semi-automatically upgrade your contracts -to breaking language changes. While it does not and cannot implement all -required changes for every breaking release, it still supports the ones, that -would need plenty of repetitive manual adjustments otherwise. - -.. note:: - - ``solidity-upgrade`` carries out a large part of the work, but your - contracts will most likely need further manual adjustments. We recommend - using a version control system for your files. This helps reviewing and - eventually rolling back the changes made. - -.. warning:: - - ``solidity-upgrade`` is not considered to be complete or free from bugs, so - please use with care. - -How it Works -~~~~~~~~~~~~ - -You can pass (a) Solidity source file(s) to ``solidity-upgrade [files]``. If -these make use of ``import`` statement which refer to files outside the -current source file's directory, you need to specify directories that -are allowed to read and import files from, by passing -``--allow-paths [directory]``. You can ignore missing files by passing -``--ignore-missing``. - -``solidity-upgrade`` is based on ``libsolidity`` and can parse, compile and -analyse your source files, and might find applicable source upgrades in them. - -Source upgrades are considered to be small textual changes to your source code. -They are applied to an in-memory representation of the source files -given. The corresponding source file is updated by default, but you can pass -``--dry-run`` to simulate to whole upgrade process without writing to any file. - -The upgrade process itself has two phases. In the first phase source files are -parsed, and since it is not possible to upgrade source code on that level, -errors are collected and can be logged by passing ``--verbose``. No source -upgrades available at this point. - -In the second phase, all sources are compiled and all activated upgrade analysis -modules are run alongside compilation. By default, all available modules are -activated. Please read the documentation on -:ref:`available modules ` for further details. - - -This can result in compilation errors that may -be fixed by source upgrades. If no errors occur, no source upgrades are being -reported and you're done. -If errors occur and some upgrade module reported a source upgrade, the first -reported one gets applied and compilation is triggered again for all given -source files. The previous step is repeated as long as source upgrades are -reported. If errors still occur, you can log them by passing ``--verbose``. -If no errors occur, your contracts are up to date and can be compiled with -the latest version of the compiler. - -.. _upgrade-modules: - -Available Upgrade Modules -~~~~~~~~~~~~~~~~~~~~~~~~~ - -+----------------------------+---------+--------------------------------------------------+ -| Module | Version | Description | -+============================+=========+==================================================+ -| ``constructor`` | 0.5.0 | Constructors must now be defined using the | -| | | ``constructor`` keyword. | -+----------------------------+---------+--------------------------------------------------+ -| ``visibility`` | 0.5.0 | Explicit function visibility is now mandatory, | -| | | defaults to ``public``. | -+----------------------------+---------+--------------------------------------------------+ -| ``abstract`` | 0.6.0 | The keyword ``abstract`` has to be used if a | -| | | contract does not implement all its functions. | -+----------------------------+---------+--------------------------------------------------+ -| ``virtual`` | 0.6.0 | Functions without implementation outside an | -| | | interface have to be marked ``virtual``. | -+----------------------------+---------+--------------------------------------------------+ -| ``override`` | 0.6.0 | When overriding a function or modifier, the new | -| | | keyword ``override`` must be used. | -+----------------------------+---------+--------------------------------------------------+ -| ``dotsyntax`` | 0.7.0 | The following syntax is deprecated: | -| | | ``f.gas(...)()``, ``f.value(...)()`` and | -| | | ``(new C).value(...)()``. Replace these calls by | -| | | ``f{gas: ..., value: ...}()`` and | -| | | ``(new C){value: ...}()``. | -+----------------------------+---------+--------------------------------------------------+ -| ``now`` | 0.7.0 | The ``now`` keyword is deprecated. Use | -| | | ``block.timestamp`` instead. | -+----------------------------+---------+--------------------------------------------------+ -| ``constructor-visibility`` | 0.7.0 | Removes visibility of constructors. | -| | | | -+----------------------------+---------+--------------------------------------------------+ - -Please read :doc:`0.5.0 release notes <050-breaking-changes>`, -:doc:`0.6.0 release notes <060-breaking-changes>`, -:doc:`0.7.0 release notes <070-breaking-changes>` and :doc:`0.8.0 release notes <080-breaking-changes>` for further details. - -Synopsis -~~~~~~~~ - -.. code-block:: none - - Usage: solidity-upgrade [options] contract.sol - - Allowed options: - --help Show help message and exit. - --version Show version and exit. - --allow-paths path(s) - Allow a given path for imports. A list of paths can be - supplied by separating them with a comma. - --ignore-missing Ignore missing files. - --modules module(s) Only activate a specific upgrade module. A list of - modules can be supplied by separating them with a comma. - --dry-run Apply changes in-memory only and don't write to input - file. - --verbose Print logs, errors and changes. Shortens output of - upgrade patches. - --unsafe Accept *unsafe* changes. - - - -Bug Reports / Feature Requests -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If you found a bug or if you have a feature request, please -`file an issue `_ on Github. - - -Example -~~~~~~~ - -Assume that you have the following contract in ``Source.sol``: - -.. code-block:: Solidity - - pragma solidity >=0.6.0 <0.6.4; - // This will not compile after 0.7.0 - // SPDX-License-Identifier: GPL-3.0 - contract C { - // FIXME: remove constructor visibility and make the contract abstract - constructor() internal {} - } - - contract D { - uint time; - - function f() public payable { - // FIXME: change now to block.timestamp - time = now; - } - } - - contract E { - D d; - - // FIXME: remove constructor visibility - constructor() public {} - - function g() public { - // FIXME: change .value(5) => {value: 5} - d.f.value(5)(); - } - } - - - -Required Changes -^^^^^^^^^^^^^^^^ - -The above contract will not compile starting from 0.7.0. To bring the contract up to date with the -current Solidity version, the following upgrade modules have to be executed: -``constructor-visibility``, ``now`` and ``dotsyntax``. Please read the documentation on -:ref:`available modules ` for further details. - - -Running the Upgrade -^^^^^^^^^^^^^^^^^^^ - -It is recommended to explicitly specify the upgrade modules by using ``--modules`` argument. - -.. code-block:: bash - - solidity-upgrade --modules constructor-visibility,now,dotsyntax Source.sol - -The command above applies all changes as shown below. Please review them carefully (the pragmas will -have to be updated manually.) - -.. code-block:: Solidity - - // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.7.0 <0.9.0; - abstract contract C { - // FIXME: remove constructor visibility and make the contract abstract - constructor() {} - } - - contract D { - uint time; - - function f() public payable { - // FIXME: change now to block.timestamp - time = block.timestamp; - } - } - - contract E { - D d; - - // FIXME: remove constructor visibility - constructor() {} - - function g() public { - // FIXME: change .value(5) => {value: 5} - d.f{value: 5}(); - } - } diff --git a/docs/yul.rst b/docs/yul.rst index 8ae454b2e0..dd29d9a498 100644 --- a/docs/yul.rst +++ b/docs/yul.rst @@ -85,7 +85,6 @@ It can be compiled using ``solc --strict-assembly``. The builtin functions It is also possible to implement the same function using a for-loop instead of with recursion. Here, ``lt(a, b)`` computes whether ``a`` is less than ``b``. -less-than comparison. .. code-block:: yul @@ -153,7 +152,7 @@ where an object is expected. Inside a code block, the following elements can be used (see the later sections for more details): -- literals, i.e. ``0x123``, ``42`` or ``"abc"`` (strings up to 32 characters) +- literals, e.g. ``0x123``, ``42`` or ``"abc"`` (strings up to 32 characters) - calls to builtin functions, e.g. ``add(1, mload(0))`` - variable declarations, e.g. ``let x := 7``, ``let x := add(y, 3)`` or ``let x`` (initial value of 0 is assigned) - identifiers (variables), e.g. ``add(3, x)`` @@ -167,6 +166,8 @@ Inside a code block, the following elements can be used Multiple syntactical elements can follow each other simply separated by whitespace, i.e. there is no terminating ``;`` or newline required. +.. index:: ! literal;in Yul + Literals -------- @@ -239,7 +240,7 @@ they have to be assigned to local variables. For built-in functions of the EVM, functional expressions can be directly translated to a stream of opcodes: You just read the expression from right to left to obtain the -opcodes. In the case of the first line in the example, this +opcodes. In the case of the second line in the example, this is ``PUSH1 3 PUSH1 0x80 MLOAD ADD PUSH1 0x80 MSTORE``. For calls to user-defined functions, the arguments are also @@ -740,7 +741,7 @@ EVM Dialect ----------- The default dialect of Yul currently is the EVM dialect for the currently selected version of the EVM. -with a version of the EVM. The only type available in this dialect +The only type available in this dialect is ``u256``, the 256-bit native type of the Ethereum Virtual Machine. Since it is the default type of this dialect, it can be omitted. @@ -751,11 +752,12 @@ This document does not want to be a full description of the Ethereum virtual mac Please refer to a different document if you are interested in the precise semantics. Opcodes marked with ``-`` do not return a result and all others return exactly one value. -Opcodes marked with ``F``, ``H``, ``B``, ``C``, ``I`` and ``L`` are present since Frontier, Homestead, -Byzantium, Constantinople, Istanbul or London respectively. +Opcodes marked with ``F``, ``H``, ``B``, ``C``, ``I``, ``L``, ``P`` and ``N`` are present since Frontier, +Homestead, Byzantium, Constantinople, Istanbul, London, Paris or Cancun respectively. In the following, ``mem[a...b)`` signifies the bytes of memory starting at position ``a`` up to -but not including position ``b`` and ``storage[p]`` signifies the storage contents at slot ``p``. +but not including position ``b``, ``storage[p]`` signifies the storage contents at slot ``p``, and +similarly, ``transientStorage[p]`` signifies the transient storage contents at slot ``p``. Since Yul manages local variables and control-flow, opcodes that interfere with these features are not available. This includes @@ -818,8 +820,6 @@ the ``dup`` and ``swap`` instructions as well as ``jump`` instructions, labels a +-------------------------+-----+---+-----------------------------------------------------------------+ | keccak256(p, n) | | F | keccak(mem[p...(p+n))) | +-------------------------+-----+---+-----------------------------------------------------------------+ -| pc() | | F | current position in code | -+-------------------------+-----+---+-----------------------------------------------------------------+ | pop(x) | `-` | F | discard value x | +-------------------------+-----+---+-----------------------------------------------------------------+ | mload(p) | | F | mem[p...(p+32)) | @@ -832,6 +832,10 @@ the ``dup`` and ``swap`` instructions as well as ``jump`` instructions, labels a +-------------------------+-----+---+-----------------------------------------------------------------+ | sstore(p, v) | `-` | F | storage[p] := v | +-------------------------+-----+---+-----------------------------------------------------------------+ +| tload(p) | | N | transientStorage[p] | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| tstore(p, v) | `-` | N | transientStorage[p] := v | ++-------------------------+-----+---+-----------------------------------------------------------------+ | msize() | | F | size of memory, i.e. largest accessed memory index | +-------------------------+-----+---+-----------------------------------------------------------------+ | gas() | | F | gas still available to execution | @@ -864,6 +868,8 @@ the ``dup`` and ``swap`` instructions as well as ``jump`` instructions, labels a +-------------------------+-----+---+-----------------------------------------------------------------+ | returndatacopy(t, f, s) | `-` | B | copy s bytes from returndata at position f to mem at position t | +-------------------------+-----+---+-----------------------------------------------------------------+ +| mcopy(t, f, s) | `-` | N | copy s bytes from mem at position f to mem at position t | ++-------------------------+-----+---+-----------------------------------------------------------------+ | extcodehash(a) | | C | code hash of address a | +-------------------------+-----+---+-----------------------------------------------------------------+ | create(v, p, n) | | F | create new contract with code mem[p...(p+n)) and send v wei | @@ -899,30 +905,36 @@ the ``dup`` and ``swap`` instructions as well as ``jump`` instructions, labels a | revert(p, s) | `-` | B | end execution, revert state changes, return data mem[p...(p+s)) | +-------------------------+-----+---+-----------------------------------------------------------------+ | selfdestruct(a) | `-` | F | end execution, destroy current contract and send funds to a | +| | | | (deprecated) | +-------------------------+-----+---+-----------------------------------------------------------------+ | invalid() | `-` | F | end execution with invalid instruction | +-------------------------+-----+---+-----------------------------------------------------------------+ -| log0(p, s) | `-` | F | log without topics and data mem[p...(p+s)) | +| log0(p, s) | `-` | F | log data mem[p...(p+s)) | +-------------------------+-----+---+-----------------------------------------------------------------+ -| log1(p, s, t1) | `-` | F | log with topic t1 and data mem[p...(p+s)) | +| log1(p, s, t1) | `-` | F | log data mem[p...(p+s)) with topic t1 | +-------------------------+-----+---+-----------------------------------------------------------------+ -| log2(p, s, t1, t2) | `-` | F | log with topics t1, t2 and data mem[p...(p+s)) | +| log2(p, s, t1, t2) | `-` | F | log data mem[p...(p+s)) with topics t1, t2 | +-------------------------+-----+---+-----------------------------------------------------------------+ -| log3(p, s, t1, t2, t3) | `-` | F | log with topics t1, t2, t3 and data mem[p...(p+s)) | +| log3(p, s, t1, t2, t3) | `-` | F | log data mem[p...(p+s)) with topics t1, t2, t3 | +-------------------------+-----+---+-----------------------------------------------------------------+ -| log4(p, s, t1, t2, t3, | `-` | F | log with topics t1, t2, t3, t4 and data mem[p...(p+s)) | +| log4(p, s, t1, t2, t3, | `-` | F | log data mem[p...(p+s)) with topics t1, t2, t3, t4 | | t4) | | | | +-------------------------+-----+---+-----------------------------------------------------------------+ | chainid() | | I | ID of the executing chain (EIP-1344) | +-------------------------+-----+---+-----------------------------------------------------------------+ | basefee() | | L | current block's base fee (EIP-3198 and EIP-1559) | +-------------------------+-----+---+-----------------------------------------------------------------+ +| blobbasefee() | | N | current block's blob base fee (EIP-7516 and EIP-4844) | ++-------------------------+-----+---+-----------------------------------------------------------------+ | origin() | | F | transaction sender | +-------------------------+-----+---+-----------------------------------------------------------------+ | gasprice() | | F | gas price of the transaction | +-------------------------+-----+---+-----------------------------------------------------------------+ | blockhash(b) | | F | hash of block nr b - only for last 256 blocks excluding current | +-------------------------+-----+---+-----------------------------------------------------------------+ +| blobhash(i) | | N | versioned hash of transaction's i-th blob, 0 if blob does not | +| | | | exist | ++-------------------------+-----+---+-----------------------------------------------------------------+ | coinbase() | | F | current mining beneficiary | +-------------------------+-----+---+-----------------------------------------------------------------+ | timestamp() | | F | timestamp of the current block in seconds since the epoch | @@ -931,6 +943,8 @@ the ``dup`` and ``swap`` instructions as well as ``jump`` instructions, labels a +-------------------------+-----+---+-----------------------------------------------------------------+ | difficulty() | | F | difficulty of the current block (see note below) | +-------------------------+-----+---+-----------------------------------------------------------------+ +| prevrandao() | | P | randomness provided by the beacon chain (see note below) | ++-------------------------+-----+---+-----------------------------------------------------------------+ | gaslimit() | | F | block gas limit of the current block | +-------------------------+-----+---+-----------------------------------------------------------------+ @@ -945,13 +959,20 @@ the ``dup`` and ``swap`` instructions as well as ``jump`` instructions, labels a The remaining bytes will retain their values as of before the call. .. note:: - With the Paris network upgrade the semantics of ``difficulty`` have been changed. - It returns the value of ``prevrandao``, which is a 256-bit value, whereas the highest recorded + The ``difficulty()`` instruction is disallowed in EVM version >= Paris. + With the Paris network upgrade the semantics of the instruction that was previously called + ``difficulty`` have been changed and the instruction was renamed to ``prevrandao``. + It can now return arbitrary values in the full 256-bit range, whereas the highest recorded difficulty value within Ethash was ~54 bits. This change is described in `EIP-4399 `_. Please note that irrelevant to which EVM version is selected in the compiler, the semantics of instructions depend on the final chain of deployment. +.. warning:: + From version 0.8.18 and up, the use of ``selfdestruct`` in both Solidity and Yul will trigger a + deprecation warning, since the ``SELFDESTRUCT`` opcode will eventually undergo breaking changes in behavior + as stated in `EIP-6049 `_. + In some internal dialects, there are additional functions: datasize, dataoffset, datacopy @@ -984,7 +1005,7 @@ Its first and only argument must be a string literal and uniquely represents the Identifiers can be arbitrary but when the compiler produces Yul code from Solidity sources, it uses a library name qualified with the name of the source unit that defines that library. To link the code with a particular library address, the same identifier must be provided to the -``--libraries`` option on the command line. +``--libraries`` option on the command-line. For example this code @@ -1065,12 +1086,12 @@ or even opcodes unknown to the Solidity compiler, care has to be taken when using ``verbatim`` together with the optimizer. Even when the optimizer is switched off, the code generator has to determine the stack layout, which means that e.g. using ``verbatim`` to modify -the stack height can lead to undefined behaviour. +the stack height can lead to undefined behavior. The following is a non-exhaustive list of restrictions on verbatim bytecode that are not checked by the compiler. Violations of these restrictions can result in -undefined behaviour. +undefined behavior. - Control-flow should not jump into or out of verbatim blocks, but it can jump within the same verbatim block. @@ -1183,8 +1204,7 @@ An example Yul Object is shown below: // executing code is the constructor code) size := datasize("Contract1_deployed") offset := allocate(size) - // This will turn into a memory->memory copy for Ewasm and - // a codecopy for EVM + // This will turn into a codecopy for EVM datacopy(offset, dataoffset("Contract1_deployed"), size) return(offset, size) } @@ -1248,7 +1268,7 @@ In Solidity mode, the Yul optimizer is activated together with the regular optim Optimization Step Sequence -------------------------- -Detailed information regrading the optimization sequence as well a list of abbreviations is +Detailed information regarding the optimization sequence as well as a list of abbreviations is available in the :ref:`optimizer docs `. .. _erc20yul: