Repository: raiden-network/raiden (language: python)

Issue:
In console link points to wrong docs
The link to the docs shown in the console still points to the old docs:

```
The Raiden API RPC server is now running at http://127.0.0.1:5001/.

See the Raiden documentation for all available endpoints at
https://docs.raiden.network/raiden-api-1
```

This should be changed to the new docs.
Fix invoking pytest without path
## Description

Until now pytest needed to be invoked with the path to the tests (`raiden/tests`) in order for the top level `conftest.py` file to be read early enough for our custom CLI options to be enabled.
Also certain other hooks (e.g. temp path shortening on macOS) are configured there and only take effect when this file is read early enough.

Having to specify the path has been a long standing irritation for both old and new contributors.

This moves the top level conftest file to the project root so that it's read early enough in all cases.

Drop invalid Metadata before sending the message
In case of direct transfers we have the following issue: 

If Metadata are invalid they should get dropped. They are going to be dropped but after the LockedTransferMessage is being sent away. This leads to the fact that the Locked Transfer will be sent to a non validated user id. 

This should not happen in the first place. Metadata need to be dropped before a Locked Transfer is passed to the transport. 
Secondly, It lead to a confusing error message because the missing metadata caused the secret reveal to fail as metadata were dropped already.

Fixing the order of dropping and creating the locked transfer should do the trick.
[WIP] Make aiortc optional
## Description

Fixes: #<issue>

Please, describe what this PR does in detail:
- If it's a bug fix, detail the root cause of the bug and how this PR fixes it.
- If it's a new feature, describe the feature this PR is introducing and design decisions.
- If it's a refactoring, describe why it is necessary. What are its pros and cons in respect to the previous code, and other possible design choices.



Discussion / hints:

Fix patch:
diff --git a/.bumpversion_client.cfg b/.bumpversion_client.cfg
index 22990ace06..bb5b58e72d 100644
--- a/.bumpversion_client.cfg
+++ b/.bumpversion_client.cfg
@@ -1,5 +1,5 @@
 [bumpversion]
-current_version = 2.0.0-a0
+current_version = 2.0.0a1
 commit = True
 tag = False
 
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 27b207be31..7e1185b3b9 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -38,6 +38,7 @@ templates:
       type: string
 
   default-environment: &default-environment
+    BENCHMARK_HOST: 'circleci'
     GETH_URL_LINUX: 'https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.1-c2d2f4ed.tar.gz'
     GETH_MD5_LINUX: '5e18a027f9919ed9ba08abd15c169f61'
     GETH_URL_MACOS: 'https://gethstore.blob.core.windows.net/builds/geth-darwin-amd64-1.10.1-c2d2f4ed.tar.gz'
@@ -143,7 +144,7 @@ commands:
             echo ${PARITY_VERSION} > /tmp/parity-version
             echo ${SOLC_VERSION} > /tmp/solc-version
       - restore_cache:
-          key: system-deps-v8-{{ arch }}-geth-{{ checksum "/tmp/geth-version" }}-parity-{{ checksum "/tmp/parity-version" }}-solc-{{ checksum "/tmp/solc-version" }}
+          key: system-deps-v9-{{ arch }}-geth-{{ checksum "/tmp/geth-version" }}-parity-{{ checksum "/tmp/parity-version" }}-solc-{{ checksum "/tmp/solc-version" }}
       - attach-and-link
       - run:
           name: Preparing environment
@@ -158,7 +159,7 @@ commands:
             .circleci/fetch_geth_parity_solc.sh
             ln -sfn ${LOCAL_BASE} ~/.local
       - save_cache:
-          key: system-deps-v8-{{ arch }}-geth-{{ checksum "/tmp/geth-version" }}-parity-{{ checksum "/tmp/parity-version" }}-solc-{{ checksum "/tmp/solc-version" }}
+          key: system-deps-v9-{{ arch }}-geth-{{ checksum "/tmp/geth-version" }}-parity-{{ checksum "/tmp/parity-version" }}-solc-{{ checksum "/tmp/solc-version" }}
           paths:
             - "~/.local-LINUX"
             - "~/.local-MACOS"
@@ -197,7 +198,7 @@ commands:
           py-version: << parameters.py-version >>
       - restore_cache:
           keys:
-          - system-deps-pyenv-v4-{{ arch }}-<< parameters.py-version >>
+          - system-deps-pyenv-v5-{{ arch }}-<< parameters.py-version >>
       - run:
           name: Installing Python on MacOS
           command: |
@@ -209,21 +210,22 @@ commands:
               env PYTHON_CONFIGURE_OPTS="--enable-shared" pyenv install $PYENV_PYTHON_VERSION -sv
             fi
       - save_cache:
-          key: system-deps-pyenv-v4-{{ arch }}-<< parameters.py-version >>
+          key: system-deps-pyenv-v5-{{ arch }}-<< parameters.py-version >>
           paths:
           - "~/.local-MACOS/.pyenv"
       - restore_cache:
-          key: pip-cache-{{ arch }}-<< parameters.py-version >>
+          key: pip-cache-v2-{{ arch }}-<< parameters.py-version >>
       - restore_cache:
           keys:
-          - python-deps-v7-{{ arch }}-<< parameters.py-version >>-{{ checksum "requirements/requirements-ci.txt" }}
-          - python-deps-v7-{{ arch }}-<< parameters.py-version >>-
+          - python-deps-v8-{{ arch }}-<< parameters.py-version >>-{{ checksum "requirements/requirements-ci.txt" }}
+          - python-deps-v8-{{ arch }}-<< parameters.py-version >>-
       - run:
           name: Creating virtualenv
           command: |
             if [[ ${OS_NAME} == "MACOS" ]]; then
               export PATH=${PYENV_ROOT}/bin:${PATH}
               eval "$(pyenv init -)"
+              eval "$(pyenv init --path)"
               pyenv local $PYENV_PYTHON_VERSION
               echo "Detected MacOS: Using pyenv Python at $(pyenv which python<< parameters.py-version >>)"
             fi
@@ -244,12 +246,12 @@ commands:
             pip-sync requirements-ci.txt _raiden-dev.txt
             popd
       - save_cache:
-          key: python-deps-v7-{{ arch }}-<< parameters.py-version >>-{{ checksum "requirements/requirements-ci.txt" }}
+          key: python-deps-v8-{{ arch }}-<< parameters.py-version >>-{{ checksum "requirements/requirements-ci.txt" }}
           paths:
           - "~/venv-<< parameters.py-version >>-LINUX"
           - "~/venv-<< parameters.py-version >>-MACOS"
       - save_cache:
-          key: pip-cache-{{ arch }}-<< parameters.py-version >>
+          key: pip-cache-v2-{{ arch }}-<< parameters.py-version >>
           paths:
           - "~/.cache/pip"
       - persist_to_workspace:
@@ -396,11 +398,13 @@ jobs:
       - run:
           name: Checkout Scenario Player
           command: |
+            cd ~
             git clone https://github.com/raiden-network/scenario-player.git
       - run:
           name: Install dependencies
           command: |
-            pip install git+https://github.com/raiden-network/scenario-player.git
+            cd ~
+            pip install ~/scenario-player
             pip install -Ue ~/raiden
       - run:
           name: Set Locale
@@ -413,13 +417,8 @@ jobs:
       - run:
           name: Run Unit Tests
           command: |
-            cd scenario-player
-            pytest .
-      - run:
-          name: Run Smoketest
-          command: |
-            cd scenario-player
-            scenario_player smoketest
+            cd ~/scenario-player
+            pytest --rootdir . tests
 
   test:
     parameters:
@@ -450,11 +449,10 @@ jobs:
       - conditional-skip
       - config-path
       - restore_cache:
-          key: ethash-{{ checksum "~/.local/bin/geth" }}
+          key: ethash-v2-{{ checksum "~/.local/bin/geth" }}
       - restore_cache:
           keys:
-            - synapse-keys-v1
-            - synapse-keys-
+            - synapse-keys-v2
       - run:
           name: Select tests
           command: .circleci/select_tests.sh "raiden/tests/<< parameters.test-type >>" "<< parameters.blockchain-type >>" << parameters.additional-args >>
@@ -465,12 +463,12 @@ jobs:
             .circleci/run_tests.sh "test-reports/test-<< parameters.test-type >>-<< parameters.transport-layer >>-<< parameters.py-version >>" "<< parameters.blockchain-type >>" "<< parameters.test-type >>" << parameters.additional-args >>
 
       - save_cache:
-          key: ethash-{{ checksum "~/.local/bin/geth" }}
+          key: ethash-v2-{{ checksum "~/.local/bin/geth" }}
           paths:
           - "~/.ethash"
 
       - save_cache:
-          key: synapse-keys-v1
+          key: synapse-keys-v2
           paths:
             - "~/.cache/synapse"
 
@@ -494,18 +492,35 @@ jobs:
       - store_artifacts:
           path: /tmp/artifacts
 
-  finalize:
+  benchmark:
     parameters:
       py-version:
         <<: *py-version-template
+
     executor:
       name: docker
-      py-version: "3.7"
+      py-version: << parameters.py-version >>
+
+    environment:
+      RAIDEN_TESTS_SYNAPSE_BASE_DIR: /home/circleci/.cache/synapse
+      RAIDEN_TESTS_SYNAPSE_LOGS_DIR: /tmp/synapse-logs
+      RAIDEN_TESTS_LOGSDIR: /tmp/tests/logs
+
     steps:
       - attach-and-link:
           py-version: << parameters.py-version >>
       - conditional-skip
       - config-path
+      - run:
+          name: Run benchmarks
+          command: |
+            python -m gevent.monkey --module pytest raiden/tests/benchmark/ -v --codespeed-url https://speed.metrics.brainbot.com
+
+  finalize:
+    executor:
+      name: docker
+      py-version: "3.7"
+    steps:
       - run:
           command: "true"
 
@@ -564,6 +579,11 @@ jobs:
             source .circleci/get_archive_tag.sh
             make bundle-docker
             echo ${RELEASE_TYPE}/raiden-${ARCHIVE_TAG}-linux-<<parameters.architecture>>.tar.gz > dist/archive/_LATEST-${RELEASE_TYPE}-linux-<<parameters.architecture>>.txt
+      - run:
+          name: Test if Binary can be launched
+          command: |
+            tar -C /tmp -xf dist/archive/raiden-${ARCHIVE_TAG}-linux-<<parameters.architecture>>.tar.gz
+            /tmp/raiden-${ARCHIVE_TAG}-linux-<<parameters.architecture>> --help
       - persist_to_workspace:
           root: "~"
           paths:
@@ -596,6 +616,10 @@ jobs:
             pyinstaller --noconfirm --clean raiden.spec
             zip -9 --junk-paths dist/archive/raiden-${ARCHIVE_TAG}-macOS-x86_64.zip dist/raiden-${ARCHIVE_TAG}-macOS-x86_64
             echo ${RELEASE_TYPE}/raiden-${ARCHIVE_TAG}-macOS-x86_64.zip > dist/archive/_LATEST-${RELEASE_TYPE}-macOS-x86_64.txt
+      - run:
+          name: Test if Binary can be launched
+          command: |
+            dist/raiden-${ARCHIVE_TAG}-macOS-x86_64 --help
       - persist_to_workspace:
           root: "~"
           paths:
@@ -613,7 +637,7 @@ jobs:
     steps:
       - add_ssh_keys:
           fingerprints:
-            - "46:b3:a5:c3:d8:21:63:cd:de:6d:07:5d:0c:5d:ba:73"
+            - "a1:33:d7:62:77:54:79:76:32:4a:d3:5e:89:70:ce:31"
       - attach-and-link:
           py-version: << parameters.py-version >>
       - conditional-skip
@@ -690,8 +714,7 @@ jobs:
                 command: |
                   rm dist/archive/*.txt
                   echo "TAG: ${CIRCLE_TAG}"
-                  CI_RAIDEN_VERSION=${CIRCLE_TAG}
-                  ghr -t ${GITHUB_TOKEN} -u ${CIRCLE_PROJECT_USERNAME} -r ${CIRCLE_PROJECT_REPONAME} -c ${CIRCLE_SHA1} -replace ${CI_RAIDEN_VERSION} dist/archive/
+                  ghr -t ${GITHUB_TOKEN_RELEASES} -u ${CIRCLE_PROJECT_USERNAME} -r ${CIRCLE_PROJECT_REPONAME} -c ${CIRCLE_SHA1} -replace ${CIRCLE_TAG} dist/archive/
 
   deploy-pypi:
     parameters:
@@ -708,19 +731,19 @@ jobs:
       - conditional-skip
       - config-path
       - run:
-          name: "install twine"
+          name: "Install Twine"
           command: pip install twine
 
       - run:
-          name: "create packages"
+          name: "Create Packages"
           command: |
             echo $(python setup.py --version)
             python setup.py sdist
             python setup.py bdist_wheel
       - run:
-          name: "upload to pypi"
+          name: "Upload to PyPI"
           command: |
-            twine upload -u $PYPI_USER -p $PYPI_PASSWORD dist/*.{tar.gz,whl}
+            twine upload -u __token__ -p $PYPI_API_TOKEN_RAIDEN dist/*.{tar.gz,whl}
 
 workflows:
   raiden-default:
@@ -748,14 +771,14 @@ workflows:
             parameters:
               py-version: ["3.7", "3.8", "3.9"]
           requires:
-            - prepare-python-linux
+            - prepare-python-linux-<< matrix.py-version >>
 
       - build-docs:
           matrix:
             parameters:
               py-version: ["3.7", "3.8", "3.9"]
           requires:
-            - prepare-python-linux
+            - prepare-python-linux-<< matrix.py-version >>
 
       - smoketest:
           matrix:
@@ -765,7 +788,7 @@ workflows:
               environment-type: ["production", "development"]
               blockchain-type: ["geth", "parity"]
           requires:
-            - lint
+            - lint-<< matrix.py-version >>
 
       - test:
           matrix:
@@ -774,7 +797,7 @@ workflows:
               py-version: ["3.7", "3.8", "3.9"]
               test-type: ["unit", "mocked"]
           requires:
-            - lint
+            - lint-<< matrix.py-version >>
 
       - test:
           matrix:
@@ -783,8 +806,17 @@ workflows:
               py-version: ["3.7", "3.8", "3.9"]
               test-type: ["fuzz"]
           additional-args: "--hypothesis-show-statistics"
+          requires:
+            - lint-<< matrix.py-version >>
+
+      - benchmark:
+          context: "raiden-general-2021"
           requires:
             - lint
+          filters:
+            branches:
+              only:
+                - develop
 
       - scenario-player-integration-test:
           name: scenario-player-integration-test
@@ -815,6 +847,7 @@ workflows:
           requires:
             - scenario-player-integration-test
             - test-integration
+            - build-docs
 
   deploy-release:
     jobs:
@@ -868,7 +901,7 @@ workflows:
             <<: *only-tagged-version-filter
 
       - deploy-digitalocean:
-          context: "Raiden Context"
+          context: "raiden-py"
           requires:
             - build-binary-linux-x86
             - build-binary-macos
@@ -876,7 +909,7 @@ workflows:
             <<: *only-tagged-version-filter
 
       - deploy-github-release:
-          context: "Raiden Context"
+          context: "raiden-general-2021"
           requires:
             - build-binary-linux-x86
             - build-binary-macos
@@ -884,7 +917,7 @@ workflows:
             <<: *only-tagged-version-filter
 
       - deploy-pypi:
-          context: "Raiden Context"
+          context: "raiden-py"
           requires:
             - build-binary-linux-x86
             - build-binary-macos
@@ -916,14 +949,14 @@ workflows:
             parameters:
               py-version: ["3.7", "3.8", "3.9"]
           requires:
-          - prepare-python-linux
+          - prepare-python-linux-<< matrix.py-version >>
 
       - build-docs:
           matrix:
             parameters:
               py-version: ["3.7", "3.8", "3.9"]
           requires:
-            - prepare-python-linux
+            - prepare-python-linux-<< matrix.py-version >>
 
       - smoketest:
           matrix:
@@ -933,7 +966,7 @@ workflows:
               environment-type: ["production", "development"]
               blockchain-type: ["geth", "parity"]
           requires:
-            - lint
+            - lint-<< matrix.py-version >>
 
       - test:
           matrix:
@@ -942,7 +975,7 @@ workflows:
               py-version: ["3.7", "3.8", "3.9"]
               test-type: ["unit", "mocked"]
           requires:
-            - lint
+            - lint-<< matrix.py-version >>
 
       - test:
           matrix:
@@ -951,8 +984,17 @@ workflows:
               py-version: ["3.7", "3.8", "3.9"]
               test-type: ["fuzz"]
           additional-args: "--hypothesis-show-statistics"
+          requires:
+            - lint-<< matrix.py-version >>
+
+      - benchmark:
+          context: "raiden-general-2021"
           requires:
             - lint
+          filters:
+            branches:
+              only:
+                - develop
 
       - scenario-player-integration-test:
           name: scenario-player-integration-test
@@ -983,6 +1025,7 @@ workflows:
           requires:
           - scenario-player-integration-test
           - test-integration
+          - build-docs
 
       - build-binary-linux:
           name: build-binary-linux-x86
@@ -1005,7 +1048,7 @@ workflows:
             - prepare-python-macos-3.7
 
       - deploy-digitalocean:
-          context: "Raiden Context"
+          context: "raiden-py"
           requires:
             - build-binary-linux-x86
             - build-binary-macos
diff --git a/.circleci/fetch_ssh_hostkey.sh b/.circleci/fetch_ssh_hostkey.sh
index a2a58cac79..1fabe9de52 100755
--- a/.circleci/fetch_ssh_hostkey.sh
+++ b/.circleci/fetch_ssh_hostkey.sh
@@ -7,6 +7,9 @@ FINGERPRINT="$2"
 
 PUBKEY=$(mktemp)
 
+mkdir -p ~/.ssh
+chmod 700 ~/.ssh
+
 ssh-keyscan -H "${HOST}" > "${PUBKEY}" 2>/dev/null
 
 if [[ $(ssh-keygen -l -f "${PUBKEY}" | cut -d ' ' -f 2) != "${FINGERPRINT}" ]]; then
diff --git a/.circleci/get_archive_tag.sh b/.circleci/get_archive_tag.sh
index 1bd5c3c0b8..cc8497676f 100755
--- a/.circleci/get_archive_tag.sh
+++ b/.circleci/get_archive_tag.sh
@@ -4,7 +4,7 @@ set -ex
 # shellcheck disable=SC2154
 if [[ -n ${CIRCLE_TAG} ]]; then
     export ARCHIVE_TAG=${CIRCLE_TAG}
-    if [[ ${CIRCLE_TAG} = "*-rc*" ]]; then
+    if [[ ${CIRCLE_TAG} =~ .*(a|b|rc).* ]]; then
         export RELEASE_TYPE="RC"
     else
         export RELEASE_TYPE="RELEASE"
diff --git a/.circleci/report_coverage.sh b/.circleci/report_coverage.sh
deleted file mode 100755
index 2ba9d607dc..0000000000
--- a/.circleci/report_coverage.sh
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/bin/bash
-
-# Upload flagged coverage reports. Mocked tests are grouped under
-# integration tests and fuzz tests under unit tests.
-
-FLAG=$1
-if [[ "${FLAG}" == "mocked" ]]
-  then FLAG=integration
-elif [[ "${FLAG}" == "fuzz" ]]
-  then FLAG=unit
-fi
-
-bash <(curl -s https://codecov.io/bash) -c -F $FLAG
-
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index e449d059aa..89ca35347c 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -15,7 +15,7 @@ default_language_version:
 
 repos:
   - repo: https://github.com/pre-commit/pre-commit-hooks
-    rev: v2.2.2
+    rev: v3.4.0
     hooks:
       - id: check-added-large-files
       - id: check-ast
@@ -26,54 +26,64 @@ repos:
       - id: check-json
       - id: check-merge-conflict
       - id: check-symlinks
+      - id: check-toml
       - id: check-vcs-permalinks
       - id: check-yaml
       - id: debug-statements
-        exclude: '^(raiden/ui/cli.py)|(raiden/tests/conftest.py)$'
+        exclude: '^(raiden/ui/cli.py)|(conftest.py)$'
       - id: detect-private-key
       - id: end-of-file-fixer
         exclude: .bumpversion_client.cfg
       - id: fix-encoding-pragma
         args: ['--remove']
-      - id: flake8
-        args: ["--config=setup.cfg"]
-        additional_dependencies: ["flake8-bugbear==18.8.0", "flake8-tuple", "readme-renderer",]
       - id: mixed-line-ending
       - id: no-commit-to-branch
         args: ['--branch', 'master', '--branch', 'develop']
       - id: trailing-whitespace
 
   - repo: https://github.com/pre-commit/pygrep-hooks
-    rev: v1.4.1
+    rev: v1.8.0
     hooks:
       - id: python-no-eval
       - id: python-no-log-warn
       - id: rst-backticks
+      - id: rst-directive-colons
+      - id: rst-inline-touching-normal
 
   - repo: git://github.com/pre-commit/mirrors-isort
-    rev: v4.3.20
+    rev: v5.8.0
     hooks:
       - id: isort
-        args: ["--ignore-whitespace", "--settings-path", "./", "--recursive"]
+        args: ["--ignore-whitespace", "--settings-path", "./"]
 
   - repo: https://github.com/python/black
-    rev: 20.8b1
+    rev: 21.4b2
     hooks:
       - id: black
 
+  - repo: https://gitlab.com/PyCQA/flake8
+    rev: 3.9.1
+    hooks:
+      - id: flake8
+        args: ["--config=setup.cfg"]
+        additional_dependencies:
+          - "flake8-bugbear==21.4.3"
+          - "flake8-tuple==0.4.1"
+          - "readme-renderer==29.0"
+
   - repo: https://github.com/pre-commit/mirrors-mypy
-    rev: v0.790
+    rev: v0.812
     hooks:
       - id: mypy
         entry: tools/pre-commit/pre-commit-wrapper.py mypy
-        additional_dependencies: ["pip-tools>=5.1.2"]
+        additional_dependencies: ["pip-tools==6.1.0"]
 
-  - repo: https://github.com/pre-commit/mirrors-pylint
-    rev: v2.3.1
+  - repo: https://github.com/PyCQA/pylint
+    rev: v2.8.2
     hooks:
       - id: pylint
         entry: tools/pre-commit/pre-commit-wrapper.py pylint
-        additional_dependencies: ["pip-tools>=5.1.2"]
+        additional_dependencies: ["pip-tools==6.1.0"]
 
   - repo: https://github.com/gruntwork-io/pre-commit
     rev: v0.1.5
diff --git a/Makefile b/Makefile
index b85322fb0c..2e8a00634f 100644
--- a/Makefile
+++ b/Makefile
@@ -47,12 +47,12 @@ JOBS_ARG=
 ifdef CIRCLECI
 JOBS_ARG=--jobs=8
 endif
-LINT_PATHS = raiden/ tools/ setup.py
+LINT_PATHS = raiden/ tools/ setup.py conftest.py
 ISORT_PARAMS = --ignore-whitespace --settings-path ./ --skip-glob '*/node_modules/*' $(LINT_PATHS)
 
 lint: ISORT_CHECK_PARAMS := --diff --check-only
 lint: BLACK_CHECK_PARAMS := --check --diff
-lint: format flake8 mypy pylint shellcheck
+lint: format flake8 mypy pylint shellcheck check-pre-commit
 
 mypy:
 	mypy raiden tools
@@ -82,6 +82,9 @@ else
 	shellcheck requirements/deps
 endif
 
+check-pre-commit:
+	python tools/pre-commit/pre-commit-check-versions.py
+
 format: isort black
 
 test:
@@ -126,7 +129,7 @@ endif
 bundle-docker: ARCHITECTURE_TAG = $(shell docker run --rm python:3.7 uname -m)
 bundle-docker: ARCHIVE_TAG ?= v$(shell python setup.py --version)
 bundle-docker:
-	docker build -t pyinstallerbuilder --build-arg GETH_URL_LINUX=$(GETH_URL_LINUX) --build-arg SOLC_URL_LINUX=$(SOLC_URL_LINUX) --build-arg ARCHITECTURE_TAG=$(ARCHITECTURE_TAG) --build-arg ARCHIVE_TAG=$(ARCHIVE_TAG) $(GITHUB_ACCESS_TOKEN_ARG) -f docker/build.Dockerfile .
+	docker build -t pyinstallerbuilder --build-arg ARCHITECTURE_TAG=$(ARCHITECTURE_TAG) --build-arg ARCHIVE_TAG=$(ARCHIVE_TAG) $(GITHUB_ACCESS_TOKEN_ARG) -f docker/build.Dockerfile .
 	-(docker rm builder)
 	docker create --name builder pyinstallerbuilder
 	mkdir -p dist/archive
@@ -143,4 +146,4 @@ check-venv:
 
 # Ensure pip-tools is installed
 check-pip-tools: check-venv
-	@command -v pip-compile > /dev/null 2>&1 || (echo "pip-tools is required. Installing." && python3 -m pip install pip-tools)
+	@command -v pip-compile > /dev/null 2>&1 || (echo "pip-tools is required. Installing." && python3 -m pip install $(shell grep pip-tools== requirements/requirements-dev.txt))
diff --git a/docker/build.Dockerfile b/docker/build.Dockerfile
index 03a6c269b9..685544f829 100644
--- a/docker/build.Dockerfile
+++ b/docker/build.Dockerfile
@@ -1,9 +1,5 @@
 FROM python:3.7-stretch
 
-# these are defined in .circleci/config.yml and passed here in the makefile
-ARG SOLC_URL_LINUX
-ARG GETH_URL_LINUX
-
 # install dependencies
 RUN apt-get update
 RUN apt-get install -y git-core wget xz-utils build-essential \
@@ -11,15 +7,6 @@ RUN apt-get install -y git-core wget xz-utils build-essential \
     libavdevice-dev libavfilter-dev libopus-dev libvpx-dev pkg-config \
     libsrtp2-dev
 
-RUN wget -nv -O /usr/bin/solc ${SOLC_URL_LINUX} && \
-    chmod +x /usr/bin/solc
-RUN wget -nv -O /tmp/geth.tar.gz ${GETH_URL_LINUX} && \
-    cd /tmp && \
-    tar xf geth.tar.gz && \
-    mv geth-linux-amd64-*/geth /usr/bin/geth && \
-    rm geth.tar.gz
-
-
 RUN python3 -m venv /venv
 ENV PATH="/venv/bin:$PATH"
 
diff --git a/docs/adr/template.rst b/docs/adr/template.rst
index c4d3d4eab7..f3e17f2658 100644
--- a/docs/adr/template.rst
+++ b/docs/adr/template.rst
@@ -1,3 +1,5 @@
+:orphan:
+
 [short title of solved problem and solution]
 ********************************************
 
diff --git a/docs/changelog.rst b/docs/changelog.rst
index 1a99cab31e..a8593f2725 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -2,25 +2,23 @@
 Changelog
 =========
 
-* :release:`2.0.0-a0 <2021-04-26>`
-* :feature:`-` This is an alpha release. Don't run it on mainnet!
-* :feature:`6851` Raiden does not use Matrix rooms anymore. This greatly reduces the load on the matrix servers and increases the reliability of the transport layer.
-* :feature:`6898` Add support for Geth 1.10.
-* :feature:`6947` Add support for Python 3.9.
-* :feature:`6821` API endpoint for UserDeposit contract deposits/withdraws
-* :bug:`6763 major` Ensure that pending messages are sent before Raiden is stopped
+* :feature:`6673` Remove refund transfers. Refunds were intended as a way to quickly unlock funds in case a transfer can't be completed without having to wait for the lock to expire. Unfortunately they were never very good at achieving this goal and added a lot of complications to the codebase. Therefore they got removed. See the linked issue for more details.
+* :feature:`-` Fix building of binary bundles which was broken for a while.
+* :feature:`6986` Increment DB version - this will make this release incompatible with previous releases. Please refer to `the known issues section in the docs <https://raiden-network.readthedocs.io/en/latest/other/known-issues.html#database-upgrades>`_ for details.
+* :feature:`-` Update WebUI to `version 1.2.1 <https://github.com/raiden-network/webui/releases/tag/v1.2.1>`_. Adds a dialog for interacting with the UserDeposit contract (UDC).
+* :feature:`6838` Add ``/notifcations`` endpoint for showing notifications and warnings to the user.
 * :feature:`6563` Faster syncing with the blockchain.
-* :feature:`-` Update WebUI to `version 1.1.1 <https://github.com/raiden-network/webui/releases/tag/v1.1.1>`_. Which introduces a new "Quick Connect" that is simpler and more transparent than the old "join token network" feature.
+* :feature:`-` Update WebUI to `version 1.1.1 <https://github.com/raiden-network/webui/releases/tag/v1.1.1>`_. Introduces a new "Quick Connect" that is simpler and more transparent.
 * :feature:`4730` Remove "join token network" feature from API. Explicitly create and fund channels, instead.
 * :feature:`6582` Add ``/settings`` endpoint with information about the used pathfinding service.
 * :feature:`6657` Support for recent geth versions.
 * :feature:`6541` Remove internal routing.
 
 * :release:`1.2.0 <2020-11-06>`
-* :feature:`6646` add [Matrix toDevice](https://matrix.org/docs/spec/client_server/r0.6.0#id70) message support in transport layer
-* :feature:`6608` Compatibility with Synapse >= 1.12 and [MSC2432](https://github.com/matrix-org/matrix-doc/pull/2432)
+* :feature:`6646` add `Matrix toDevice <https://matrix.org/docs/spec/client_server/r0.6.0#id70>`_ message support in transport layer
+* :feature:`6608` Compatibility with Synapse >= 1.12 and `MSC2432 <https://github.com/matrix-org/matrix-doc/pull/2432>`_
 * :feature:`6482` Add support for client capabilities. Raiden clients can now negotiate supported features via their matrix profile.
-* :bug:`6444 major` Fix a race condition when processing blockchain events.
+* :bug:`6444` Fix a race condition when processing blockchain events.
 
 * :release:`1.1.1 <2020-07-20>`
 * :feature:`-` Update WebUI to version 1.0.2 https://github.com/raiden-network/webui/releases/tag/v1.0.2
diff --git a/docs/conf.py b/docs/conf.py
index 10aa025166..3f0e10067f 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -56,7 +56,7 @@ def check_if_nightly(version):
 
 # Add customized stylesheet
 def setup(app):
-    app.add_stylesheet("css/custom.css")
+    app.add_css_file("css/custom.css")
 
 
 # -- General configuration ------------------------------------------------
@@ -100,7 +100,7 @@ def setup(app):
 project = "Raiden Network"
 author = "Raiden Project"
 
-version_string = "2.0.0"
+version_string = "2.0.0rc3"
 # The version info for the project you're documenting, acts as replacement for
 # |version| and |release|, also used in various other places throughout the
 # built documents.
@@ -137,7 +137,7 @@ def setup(app):
 # List of patterns, relative to source directory, that match files and
 # directories to ignore when looking for source files.
 # This patterns also effect to html_static_path and html_extra_path
-exclude_patterns = ['**/*.inc.rst']
+exclude_patterns = ["**/*.inc.rst"]
 
 # The reST default role (used for this markup: `text`) to use for all
 # documents.
diff --git a/docs/custom-setups/custom_token.rst b/docs/custom-setups/custom_token.rst
new file mode 100644
index 0000000000..5ab78ecd02
--- /dev/null
+++ b/docs/custom-setups/custom_token.rst
@@ -0,0 +1,108 @@
+Use Custom Token
+################
+
+Introduction
+============
+
+In this guide, we will walk through the process of creating a custom token. 
+A custom token can be used for any use-case but we will be adding and using it with the Raiden Network.
+In order to deploy the Custom Token, you will need to:
+
+- :ref:`Get Goerli Testnet ETH (GÖETH) <custom_token_get_eth>`
+- :ref:`Import Custom Token <custom_token_import>`
+- :ref:`Compile Imported Token Code <custom_token_compile>`
+- :ref:`Deploy Custom Token <custom_token_deploy>`
+
+.. _custom_token_get_eth:
+
+Get Goerli Testnet ETH (GÖETH)
+==============================
+
+Goerli is the name of the testnet on Ethereum where we will be deploying our token. 
+To pay for the on-chain transactions you'll first need to get some Goerli ETH (GÖETH) on your Ethereum account.
+
+Since we will be using the Goerli testnet you'll have to switch from **"Main Ethereum Network"** to **"Goerli Test Network"** in your MetaMask by selecting it from the top right dropdown.
+
+After switching to the Goerli testnet you can go ahead and acquire some Goerli testnet ETH by following these steps:
+
+1. Copy your address. It is located on the left hand side in MetaMask and starts with "0x".
+2. Visit the `Goerli faucet <https://goerli-faucet.slock.it/>`_, there you'll have to paste the address into the input field and check the *"I'm not a robot"*  checkbox.
+3. Click on **"REQUEST 0.05 GÖETH"**.
+
+You will see a success message when the request has gone through. If it fails, keep trying until it succeeds.
+
+You can verify that 0.05 GÖETH was actually received by opening MetaMask and see if your Goerli Test Network shows a balance of total 0.05 GÖETH.
+
+
+.. _custom_token_import:
+
+Import Custom Token
+===================
+
+The Raiden Network supports transactions of ERC20 compliant tokens. Any smart contract that implements the ERC20 interface can be used with Raiden.
+
+Importing the custom token Solidity code is done via the `Remix Ethereum IDE <https://remix.ethereum.org/>`_ (Ethereum's integrated development environment). 
+You can read more about ERC20 tokens in `this GitHub repo <https://github.com/OpenZeppelin/openzeppelin-contracts/tree/master/contracts/token/ERC20>`_.
+
+.. image:: remix_gist_import.png
+
+1. Start off by getting the Solidity code for an ERC20 token from `this GitHub gist <https://gist.github.com/eorituz/a779c0277f2fc5c935e2b2f62e49da49>`_.
+2. Enter the URL for the `Remix Ethereum IDE <https://remix.ethereum.org/>`_ in your browser.
+3. Under **"Environments"** click **"Solidity"**.
+4. Under **"File"** choose to import from **"Gist"**.
+5. In the pop-up that appears enter the ID, *a779c0277f2fc5c935e2b2f62e49da49*, of the gist you just viewed.
+
+If the import was successful you should be able to open the imported code from the file explorer on the left hand side menu.
+
+
+.. _custom_token_compile:
+
+Compile Imported Token Code
+===========================
+
+Before a token can be deployed the imported token code has to be compiled into Ethereum Virtual Machine Code.
+
+Click on the icon underneath the **"FILE EXPLORER"** in the left hand side menu to view the **"SOLIDITY COMPILER"**.
+
+.. image:: remix_compile2.png
+
+In the compiler you need to:
+
+1. Select **"0.5.4+commit..."** from the "Compiler" dropdown.
+2. Click on **"Compile CustomErc20Token.sol"**.
+
+This will compile the token code and once the compiling step is done you’re ready to deploy your custom token.
+
+
+.. _custom_token_deploy:
+
+Deploy Custom Token
+===================
+
+In this step, we will be setting all parameters and finally deploy the custom token to the Goerli testnet.
+
+Navigate to **"DEPLOY & RUN TRANSACTIONS"** in the left hand side menu.
+
+.. image:: remix_deploy.png
+
+1. Select **"Injected Web3"** from the "Environment" dropdown. This will open up MetaMask.
+2. Connect with MetaMask when prompted.
+3. Click on the arrow next to the orange **"Deploy"** button to expand the configure parameters.
+4. Use the following parameter values:
+
+   -  **initial_supply** **= 10000**
+   -  **decimal_units = 0**
+   -  **token_name = MyToken**
+   -  **token_symbol = TKN**
+
+5. Click the orange **"transact"** button.
+6. Confirm the transaction from MetaMask when prompted.
+
+In the Remix Ethereum IDE terminal you should now see an output similar to the picture below. 
+Congratulations, you have now successfully deployed a token to Ethereum!
+
+.. image:: remix_deploy3.png
+
+You can click the link in the terminal output that starts with "https://goerli.etherscan.io..." to open a new tab with all the details of the transaction you just sent.
+
+All you need to do to start using your token is to :doc:`download and install Raiden <../overview_and_guide>` and register your token in Raiden, either in the :ref:`Web Interface <webui_register_token>` or by using the :ref:`API <api_tut_register_token>`.
diff --git a/docs/private_net_tutorial.rst b/docs/custom-setups/private_net_tutorial.rst
similarity index 100%
rename from docs/private_net_tutorial.rst
rename to docs/custom-setups/private_net_tutorial.rst
diff --git a/docs/custom-setups/remix_compile2.png b/docs/custom-setups/remix_compile2.png
new file mode 100644
index 0000000000..55719b7b76
Binary files /dev/null and b/docs/custom-setups/remix_compile2.png differ
diff --git a/docs/custom-setups/remix_deploy.png b/docs/custom-setups/remix_deploy.png
new file mode 100644
index 0000000000..2950062ef1
Binary files /dev/null and b/docs/custom-setups/remix_deploy.png differ
diff --git a/docs/custom-setups/remix_deploy3.png b/docs/custom-setups/remix_deploy3.png
new file mode 100644
index 0000000000..6565f9db08
Binary files /dev/null and b/docs/custom-setups/remix_deploy3.png differ
diff --git a/docs/custom-setups/remix_gist_import.png b/docs/custom-setups/remix_gist_import.png
new file mode 100644
index 0000000000..5eaf57d55b
Binary files /dev/null and b/docs/custom-setups/remix_gist_import.png differ
diff --git a/docs/custom_setups.rst b/docs/custom_setups.rst
new file mode 100644
index 0000000000..007a37cfcc
--- /dev/null
+++ b/docs/custom_setups.rst
@@ -0,0 +1,12 @@
+Custom Setups
+#############
+
+This section provides tutorials for using Raiden in custom setups.
+You can either read about how to :doc:`run Raiden on a private Ethereum network <custom-setups/private_net_tutorial>` or how to :doc:`create a custom token <custom-setups/custom_token>` for using it with Raiden in a testnet.
+
+
+.. toctree::
+  :maxdepth: 2
+
+  custom-setups/private_net_tutorial
+  custom-setups/custom_token
diff --git a/docs/getting_started.rst b/docs/getting_started.rst
index 9e1def63e1..bad3a4cae8 100644
--- a/docs/getting_started.rst
+++ b/docs/getting_started.rst
@@ -3,24 +3,15 @@ Getting Started
 
 Now that your Raiden node is up and running, you can choose from the following tutorials, depending on your Raiden use case:
 
-Where do you want to use Raiden?
-================================
+See :doc:`Using Raiden on Mainnet <using-raiden-on-mainnet/overview>` for getting started to use Raiden on the Ethereum mainnet.
+The :doc:`Raiden Web Interface <the-raiden-web-interface/the-raiden-web-interface>` is the easiest way to use Raiden interactively. 
+For programmatic use of Raiden, see :doc:`The Raiden API Tutorial <raiden-api-1/api-tutorial/README>`.
 
-See :doc:`Using Raiden on Mainnet <using-raiden-on-mainnet/overview>` for using Raiden in the Ethereum mainnet or :doc:`Using Raiden on Testnet <using-raiden-on-testnet/quick-start-on-goerli-testnet>` to try Raiden with test tokens on the Görli testnet.
 
 .. toctree::
   :maxdepth: 2
 
-  Using Raiden on Mainnet <using-raiden-on-mainnet/overview.rst>
-  Using Raiden on Testnet <using-raiden-on-testnet/quick-start-on-goerli-testnet.rst>
-
-How do you want to use Raiden?
-==============================
-
-The :doc:`Raiden Web Interface <the-raiden-web-interface/the-raiden-web-interface>` is the easiest way to use Raiden interactively. For programmatic use of Raiden, see :doc:`The Raiden API Tutorial <raiden-api-1/api-tutorial/README>`.
-
-.. toctree::
-  :maxdepth: 2
-
-  Raiden Web Interface <the-raiden-web-interface/the-raiden-web-interface.rst>
+  Using Raiden on Mainnet Guide <using-raiden-on-mainnet/overview.rst>
+  Raiden Web Interface Tutorial <the-raiden-web-interface/the-raiden-web-interface.rst>
   Raiden API Tutorial <raiden-api-1/api-tutorial/README.rst>
+
diff --git a/docs/glossary.rst b/docs/glossary.rst
index 54a41581cf..c7aa966c3d 100644
--- a/docs/glossary.rst
+++ b/docs/glossary.rst
@@ -1,86 +1,124 @@
 Raiden Glossary
 ===============
 
+Payments and Transfers
+----------------------
+
 .. glossary::
-   :sorted:
+    :sorted:
+
+    MediatedTransfer
+        A mediated transfer is a hashlocked transfer between an initiator and a target propagated through nodes in the network.
+
+    RefundTransfer
+        A refund transfer is a special type of :term:`MediatedTransfer` that is used when a node can no longer propagate a transfer and a routing backtrack needs to be done.
+
+    Reveal timeout
+        The number of blocks in a channel allowed for learning about a secret being revealed through the blockchain and acting on it.
 
-   payer
-       In a channel relationship between two raiden nodes the payer is the participant who sends a transfer.
+    Payment channel
+        The on-chain payment channel between two Raiden nodes.
 
-   payee
-       In a channel relationship between two raiden nodes the payee is the participant who receives a transfer
+    Settlement timeout
+    Settlement window
+        The number of blocks after the closing of a channel within which the :term:`counterparty` is able to call ``updateNonClosingBalanceProof`` with the latest :term:`balance proof` they received.
 
-   initiator
-       In a payment the initiator is the raiden node which starts the payment
+    Merkletree root
+    Locksroot
+        The root of the ``merkle tree`` which holds the hashes of all the locks in the channel.
 
-   target
-       In a payment the target is the raiden node for which the payment sent by the initiator is intended
+    Transfer
+        A transfer in Raiden happens each time tokens are sent inside a :term:`payment channel`.
 
-   MediatedTransfer
-       A mediated transfer is a hashlocked transfer between an initiator and a target propagated through nodes in the network.
+    Payment
+        A payment in Raiden is the process of sending tokens from one account to another. Each payment has an :term:`initiator` and a :term:`target` and can be composed of multiple transfers.
 
-   RefundTransfer
-       A refund transfer is a special type of MediatedTransfer that is used when a node can no longer propagate a transfer and a routing backtrack needs to be done.
+    Transferred amount
+        The transferred amount is the total amount of tokens sent from a participant's account to the account of a :term:`counterparty`.
 
-   reveal timeout
-       The number of blocks in a channel allowed for learning about a secret being revealed through the blockchain and acting on it.
+    Locked amount
+        The locked amount is the total amount of tokens one participant of a payment channel has locked in pending transfers towards his :term:`counterparty`
 
-   payment channel
-       The on-chain payment channel between two raiden nodes.
+    Channel capacity
+        The channel capacity determines how many tokens a channel holds. You can calculate the capacity by either:
 
-   settlement timeout
-   settlement window
-       The number of blocks after the closing of a channel within which the counterparty is able to call ``updateNonClosingBalanceProof`` with the latest :term:`balance proof` they received.
+        * Taking the total amount of tokens deposited and subtracting the total amount of tokens withdrawn by both participants that have a channel open with each other.
+        * Taking the sum of both channel participants' :term:`balance`.
 
-   merkletree root
-   locksroot
-       The root of the ``merkle tree`` which holds the hashes of all the locks in the channel.
+    Balance
+        The balance determines how many tokens one specific channel participant holds.
 
-   transfer
-       In Raiden a transfer denotes a single hop transfer of tokens, either direct or hash time locked inside a payment channel.
+        You can calculate the balance by taking the total amount of tokens deposited, adding the total amount of tokens received and subtracting the total amount of tokens sent for a participant.
 
-   payment
-       In Raiden a payment denotes the process of sending tokens from one account to another. A payment has an initiator and a target and can be composed of multiple transfers.
+        :math:`B_{participant} = P_{total\ token\ deposit} + P_{total\ tokens\ received} - P_{total\ tokens\ sent}`
 
-   transferred amount
-       The transferred amount is the total amount of tokens one participant of a payment channel has sent to his counterparty.
+    Locked balance
+        The locked balance of a channel participant is the sum of the locked amount for all pending transfers :math:`T_{pending}`.
+        
+        :math:`B_{locked} = \sum_{k=0}^{N-1} T_{pending}` where :math:`N` is the number of pending transfers
 
-   locked amount
-       The locked amount is the total amount of tokens one participant of a payment channel has locked in pending transfers towards his counterparty
+    Available balance
+        The available balance of a channel participant is: 
+        
+        :math:`B_{available} = B_{participant} - B_{locked}`
 
-   channel capacity
-       A channel's capacity is the sum of the total deposits minus the sum of the total withdraws of both its participants. It is also the sum of the channel participants :term:`balance`.
+    Balance proof
+        Balance proof is any kind of message used in order to cryptographically prove on the blockchain what the latest :term:`transferred amount` and :term:`locked amount` received from a counterparty is.
 
-   balance
-       The balance :math:`B_n` of a channel participant :math:`P` is his total deposit :math:`P_d` along with the amount of tokens he received :math:`P_r` minus the amount :math:`P_s` of token he has sent. So :math:`B_n = P_d + P_r - P_s`
+    Hashlock
+        A hashlock is the hashed secret that accompanies a locked message: ``sha3(secret)``.
 
-   locked balance
-       The locked balance :math:`B_l` of a channel participant is the sum of the locked amount for all pending transfers :math:`T_p`. So :math:`B_l = \sum_{k=0}^{N-1} T_p` where :math:`N` is the number of pending transfers.
+    Lock expiration
+        The lock expiration is the highest ``block_number`` until which the transfer can be settled.
 
-   available balance
-       The available balance :math:`B_a` of a channel participant is :math:`B_a = B_n - B_l`.
+    SecretRequest
+        The secret request message is sent by the target of a mediated transfer to its initiator in order to request the secret to unlock the transfer.
 
-   balance proof
-       Balance proof is any kind of message used in order to cryptographically prove on the blockchain what the latest :term:`transferred amount` and :term:`locked amount` received from a counter party is.
+    RevealSecret
+        The reveal secret message is sent to a node that is known to have an interest to learn the secret.
 
-   hashlock
-       A hashlock is the hashed secret that accompanies a locked message: ``sha3(secret)``.
+    Secret message
+        The secret message is a message containing the secret and used for synchronization between mediated transfer participants.
 
-   lock expiration
-       The lock expiration is the highest block_number until which the transfer can be settled.
+    Preimage
+    Secret
+        The preimage, what we call the secret in Raiden, is 32 bytes of random cryptographically secure data whose keccak hash ends up being the :term:`hashlock`.
 
-   SecretRequest
-       The secret request message is sent by the target of a mediated transfer to its initiator in order to request the secret to unlock the transfer.
 
-   RevealSecret
-       The reveal secret message is sent to a node that is known to have an interest to learn the secret.
+Participants
+------------
 
-   secret message
-       The secret message is a message containing the secret and used for synchronization between mediated transfer participants.
+Overview of a payment with one mediator:
+
+.. code:: text
+
+                 Transfer1               Transfer2
+    [Initiator] -----------> [Mediator] -----------> [Target]
+                Payer  Payee            Payer  Payee
+
+.. glossary::
+    :sorted:
 
-   counterparty
-       The counterparty of a channel is the other participant of the channel that is not ourselves.
+    Counterparty
+        The counterparty of a channel is the other channel participant with whom you have opened a channel.
+
+    Initiator
+        The initiator is the Raiden node which initiates (starts) a :term:`payment`.
+
+    Payer
+        The payer is the participant who sends a :term:`transfer`.
+
+    Payee
+        The payee is the participant who receives a :term:`transfer`.
+
+    Target
+        The target is the Raiden node which receives a payment from the :term:`initiator`.
+
+Services
+--------
+
+.. glossary::
+    :sorted:
 
-   preimage
-   secret
-       The preimage, what we call the secret in Raiden, is 32 bytes of random cryptographically secure data whose keccak hash ends up being the :term:`hashlock`.
+    User Deposit
+        The Raiden services will ask for payment in RDN. The Monitoring Service and the Pathfinding Service require deposits to be made in advance of service usage. These deposits are handled by the User Deposit Contract.
\ No newline at end of file
diff --git a/docs/index.rst b/docs/index.rst
index 48b81a838f..87c0363e8a 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -1,48 +1,44 @@
 Raiden Network |capitalized_version| Documentation
 **************************************************
 
-
-This is the documentation for the |version| version of the `Raiden Network <http://raiden.network/>`_. You can find the source code in `our Github Repository <https://github.com/raiden-network/raiden>`_.
+.. note::
+  This is the documentation for the |version| version of the `Raiden Network <http://raiden.network/>`_. You can find the source code in `our Github Repository <https://github.com/raiden-network/raiden>`_.
 
 What is the Raiden Network?
 ---------------------------
-The Raiden Network is an off-chain scaling solution, enabling near-instant, low-fee and scalable payments. It’s complementary to the Ethereum blockchain and works with any ERC20 compatible token. The Raiden project is work in progress. Its goal is to research state channel technology, define protocols and develop reference implementations.
-
-How to get started
-------------------
-
-Whether you've just heard of Raiden for the first time or you're here looking for something specific here's a short list of relevant resources and links.
+The Raiden Network is an off-chain scaling solution, enabling fast, low-fee and scalable token transfers. It’s complementary to the Ethereum blockchain and works with any ERC20 compatible token.
 
 New to Raiden?
-~~~~~~~~~~~~~~
-If you're new to Raiden and just want to install and try it out, you can either:
+--------------
 
-    * Use the `Raiden Wizard <https://docs.raiden.network/quick-start/>`_ for quickly installing and running a Raiden node.
-    * :doc:`Read about different options for installing and firing up <overview_and_guide>` a Raiden client.
-    * Check out the `WebUI tutorial <https://docs.raiden.network/the-raiden-web-interface/the-raiden-web-interface>`_ to quickly get started doing payments.
+If you’re new to Raiden and just want to install it and try it out, go to the :doc:`Quick Start <installation/quick-start/README>`.
+Afterwards you can do token transfers using the :doc:`Raiden WebUI <the-raiden-web-interface/the-raiden-web-interface>`.
+
+For a more advanced usage, read about the different options for :doc:`installing and firing up <overview_and_guide>` a Raiden client.
 
 Developing on Raiden?
-~~~~~~~~~~~~~~~~~~~~~
-If you're looking for documentation relevant for building on top of Raiden:
+---------------------
+If you want to use Raiden for your project the following links have relevant documentation:
 
-    * Read the thorough guide to `get started with the Raiden API <https://docs.raiden.network/raiden-api-1/api-tutorial>`_.
-    * Checkout the complete :doc:`API documentation <rest_api>`.
-    * Read the document :doc:`How is Raiden Tested <testing>`.
+* Check out the `examples on the developer portal <https://developer.raiden.network/#Examples>`_
+* Read a detailed guide on how to :doc:`get started using the Raiden API <raiden-api-1/api-tutorial/README>`
+* Read the complete :doc:`documentation of the Raiden API <rest_api>`
 
 Contributing to Raiden?
-~~~~~~~~~~~~~~~~~~~~~~~
-If you're looking for a more in depth understanding of Raiden and are interested in contributing:
+-----------------------
+If you are looking to get an in-depth understanding of Raiden or if you're interested in contributing to Raiden, a good start is to:
 
-    * Take a look at the :doc:`onboarding guide <onboarding>`.
-    * Check out the code in our `Github repository <https://github.com/raiden-network/raiden>`_.
-    * Read the `Raiden specification <https://raiden-network-specification.readthedocs.io/en/latest/index.html>`_.
+* Read the :doc:`on-boarding guide <onboarding>`
+* Read the `Raiden specification <https://raiden-network-specification.readthedocs.io/en/latest/index.html>`_.
+* Take a look at the `source code <https://github.com/raiden-network/raiden>`_
+* Read the "tech deep dive" series on our `blog <https://medium.com/raiden-network>`_
 
 Need help?
-~~~~~~~~~~
-If you run in to any kind of problems or you discover bugs:
+----------
+If you run into problems or discover bugs, you can:
 
-    * Ask for help or project related questions in the `Raiden gitter <https://gitter.im/raiden-network/raiden>`_.
-    * Create a `Github issue <https://github.com/raiden-network/raiden/issues/new/choose>`_ if you discover a bug or have a feature request.
+* Ask for help in the `Raiden gitter chat <https://gitter.im/raiden-network/raiden>`_
+* Create a `Github issue <https://github.com/raiden-network/raiden/issues/new/choose>`_ for either a bug report or feature request
 
 Disclaimer
 ----------
@@ -50,7 +46,7 @@ Please note, that even though we do our best to ensure the quality and accuracy
 The wording and concepts regarding financial terminology (e.g. “payments”, “checks”, “currency”, “transfer” [of value]) are exclusively used in an exemplary way to describe technological principles and do not necessarily conform to the real world or legal equivalents of these terms and concepts.
 
 Contents
-********
+--------
 
 .. toctree::
   :maxdepth: 2
@@ -60,7 +56,7 @@ Contents
   getting_started
   rest_api
   raiden_services
-  private_net_tutorial
+  custom_setups
 
 .. toctree::
   :maxdepth: 2
@@ -68,6 +64,7 @@ Contents
 
   onboarding
   proxies
+  testing
   changelog
 
 .. toctree::
@@ -75,6 +72,7 @@ Contents
   :caption: Other
 
   other/safe-usage
+  other/slow-network-test
   other/known-issues
   config_file
   glossary
diff --git a/docs/using-raiden-on-testnet/wizard_goerli.gif b/docs/installation/quick-start-goerli/wizard_goerli.gif
similarity index 100%
rename from docs/using-raiden-on-testnet/wizard_goerli.gif
rename to docs/installation/quick-start-goerli/wizard_goerli.gif
diff --git a/docs/installation/quick-start/README.rst b/docs/installation/quick-start/README.rst
index a4aed268bf..e0bea09458 100644
--- a/docs/installation/quick-start/README.rst
+++ b/docs/installation/quick-start/README.rst
@@ -1,7 +1,10 @@
+.. _wizard_overview:
+
 Overview
 ########
 
-In this tutorial we will walk through the steps to:
+This quick start section will help you install Raiden and run a Raiden node on the Ethereum mainnet using the Raiden Wizard.
+We will walk through the steps to:
 
 -  `Get MetaMask`_
 -  `Get an Infura ID`_
@@ -12,11 +15,6 @@ In this tutorial we will walk through the steps to:
     If you want to try out Raiden on a testnet before running it on mainnet,
     the :ref:`Quick start on Görli page <quickstart_goerli>` will help you.
 
-.. include:: get-metamask.rst
-.. include:: infura-quick-start.rst
-.. include:: download-and-run-the-raiden-wizard.rst
-.. include:: known-issues.rst
-
 
 Prerequisites
 #############
@@ -28,6 +26,13 @@ In order to use the Raiden Wizard and Raiden you'll need:
    ``0.13`` ETH
 -  An `Infura <https://infura.io>`__ ID
 
+
+.. include:: get-metamask.inc.rst
+.. include:: infura-quick-start.inc.rst
+.. include:: download-and-run-the-raiden-wizard.inc.rst
+.. include:: known-issues.inc.rst
+
+
 Need help?
 ##########
 
diff --git a/docs/installation/quick-start/download-and-run-the-raiden-wizard.rst b/docs/installation/quick-start/download-and-run-the-raiden-wizard.inc.rst
similarity index 93%
rename from docs/installation/quick-start/download-and-run-the-raiden-wizard.rst
rename to docs/installation/quick-start/download-and-run-the-raiden-wizard.inc.rst
index f900767417..ac16a7105d 100644
--- a/docs/installation/quick-start/download-and-run-the-raiden-wizard.rst
+++ b/docs/installation/quick-start/download-and-run-the-raiden-wizard.inc.rst
@@ -1,8 +1,8 @@
 Download and run the Raiden Wizard
-==================================
+##################################
 
 Download the Raiden Wizard
---------------------------
+==========================
 
 `Download <https://github.com/raiden-network/raiden-wizard/releases/latest>`__
 the Raiden Wizard for:
@@ -11,7 +11,7 @@ the Raiden Wizard for:
 -  Linux
 
 Run the Raiden Wizard
----------------------
+=====================
 
 .. warning::
 
diff --git a/docs/installation/quick-start/get-metamask.rst b/docs/installation/quick-start/get-metamask.inc.rst
similarity index 92%
rename from docs/installation/quick-start/get-metamask.rst
rename to docs/installation/quick-start/get-metamask.inc.rst
index 9c12296369..ae108e60cb 100644
--- a/docs/installation/quick-start/get-metamask.rst
+++ b/docs/installation/quick-start/get-metamask.inc.rst
@@ -1,8 +1,8 @@
 Get MetaMask
-============
+############
 
 Install MetaMask
-----------------
+================
 
 MetaMask is an in-browser Ethereum wallet. It allows us to send and sign
 transactions on the Ethereum blockchain.
@@ -15,7 +15,7 @@ To install MetaMask:
    right corner of your browser.
 
 Create an Ethereum account
---------------------------
+==========================
 
 With the newly installed MetaMask extension click the icon in your
 browser. This will open up MetaMask with a setup guide for creating a
diff --git a/docs/installation/quick-start/infura-quick-start.rst b/docs/installation/quick-start/infura-quick-start.inc.rst
similarity index 94%
rename from docs/installation/quick-start/infura-quick-start.rst
rename to docs/installation/quick-start/infura-quick-start.inc.rst
index e82df66796..e094521cb8 100644
--- a/docs/installation/quick-start/infura-quick-start.rst
+++ b/docs/installation/quick-start/infura-quick-start.inc.rst
@@ -1,5 +1,7 @@
+.. _infura_tutorial:
+
 Get an Infura ID
-================
+################
 
 .. note::
    **Why do I need Infura?**
diff --git a/docs/installation/quick-start/known-issues.rst b/docs/installation/quick-start/known-issues.inc.rst
similarity index 79%
rename from docs/installation/quick-start/known-issues.rst
rename to docs/installation/quick-start/known-issues.inc.rst
index 5540966e3b..f2eee82c15 100644
--- a/docs/installation/quick-start/known-issues.rst
+++ b/docs/installation/quick-start/known-issues.inc.rst
@@ -1,16 +1,16 @@
 Handle Limitations
-==================
+##################
 
 Internal Server Error
----------------------
+=====================
 
 The Raiden Wizard will display an **Internal Server Error** if an
-invalid project ID is provided. To solve this you have to `manually
+invalid project ID is provided. To solve this you have to :ref:`manually
 delete the configuration
-file <Delete configuration files>`__ that got created.
+file <wizard_delete_config>` that got created.
 
 Stop Raiden from running
-------------------------
+========================
 
 The Wizard does not provide a way of shutting down the Raiden node. You
 have to cancel the process to stop Raiden.
@@ -21,6 +21,8 @@ Raiden.
 **Linux:** Use any Linux process manager for stopping
 Raiden.
 
+.. _wizard_delete_config:
+
 Delete configuration files
 --------------------------
 
diff --git a/docs/macos_install_guide.rst b/docs/macos_install_guide.rst
index 6e2bff7a94..44b06c3944 100644
--- a/docs/macos_install_guide.rst
+++ b/docs/macos_install_guide.rst
@@ -59,7 +59,7 @@ The following instructions will guide you from a clean installation of macOS to
     $ make install-dev
 
 Now you have a working source installation of Raiden.
-To actually use it you also need the solidity compiler solc_ and an `Ethereum client`_.
+To actually use it you also need the Solidity compiler solc_ and an `Ethereum client`_.
 Both can be installed as follows::
 
     $ brew tap ethereum/ethereum
diff --git a/docs/manual-udc-deposit.rst b/docs/manual-udc-deposit.rst
new file mode 100644
index 0000000000..c1b1786b8f
--- /dev/null
+++ b/docs/manual-udc-deposit.rst
@@ -0,0 +1,55 @@
+:orphan:
+
+.. _manual_udc_deposit:
+
+Manually depositing tokens to pay the services
+==============================================
+
+To pay the services, you have to lock some of your Raiden tokens in the ``UserDeposit`` contract.
+Normally the Raiden Wizard or some other auxilliary scripts would do the contract interaction for you.
+In this section, we will briefly explain how it can be done manually so you are able to use Raiden without them.
+
+All services that are registered in the service registry of a given network will use one shared instance of ``UserDeposit``.
+You can obtain the address of the contract from
+
+    ``https://github.com/raiden-network/raiden-contracts/blob/master/raiden_contracts/data/deployment_services_<network>.json``
+
+where ``network`` is one of ``mainnet``, ``ropsten``, ``rinkeby`` or ``goerli``.
+
+As an example, suppose we want to run a Raiden node with address ``0x3040435D7F1012e861f0B0989422a47D1825F120`` on the mainnet
+and deposit 10 Raiden tokens (10**19 REI) for it to use. Here we use `MetaMask <https://metamask.io>`_ to access our wallet
+and the contract interface of `Etherscan <https://etherscan.io>`_ to do the transactions. Of course, you can also use another
+service or your own Ethereum node.
+
+We log into MetaMask with our account that holds the Raiden tokens (which should be a different account than the one we use with
+Raiden, as the latter is supposed to be used with Raiden only.) To find the ``UserDeposit`` contract, we take a look at ``deployment_services_mainnet.json``:
+
+.. code-block:: none
+
+    {
+        "contracts_version": null, "chain_id": 1,
+        (...)
+        "UserDeposit": {
+            "address": "0x53Cc1decDD7d452c8844a5f383e23AD479A1f614",
+            (...)
+
+
+We can then look up the contract address on Etherscan, and use Etherscan's "read/write contract" panels to interact with it.
+The Raiden token (RDN) can be searched by name on Etherscan, or we can look up its address in the ``UserDeposit`` contract's
+``token`` property. On the testnets, the token symbol is SVT (service token) rather than RDN and it may not be possible
+to find the token by name, but it can always be found in ``UserDeposit.token``.
+
+
+As usual with ERC-20 tokens, we need to call two contract functions:
+
+.. code-block:: none
+
+    approve(0x53Cc1decDD7d452c8844a5f383e23AD479A1f614, 10000000000000000000)
+
+on the RDN (or SVT) token contract, to allow the ``UserDeposit`` contract to move the 10 RDN, and
+
+.. code-block:: none
+
+    deposit(0x3040435D7F1012e861f0B0989422a47D1825F120, 10000000000000000000)
+
+on the ``UserDeposit`` contract.
\ No newline at end of file
diff --git a/docs/onboarding.rst b/docs/onboarding.rst
index 50248ea3ac..a981db9029 100644
--- a/docs/onboarding.rst
+++ b/docs/onboarding.rst
@@ -232,7 +232,7 @@ The reasoning behind the choice of Matrix as a transport layer for Raiden is nic
 
 In this section we are going to only look over the main points in the code which are the entries to the transport layer. For more information please check out the matrix transport `spec <https://raiden-network-specification.readthedocs.io/en/latest/transport.html>`_.
 
-In Matrix each user has a userid in the form of ``@<userId>:<homeserver_uri>``. ``userId`` is the lowercased ethereum address of the node, possibly followed by a 4-bytes hex-encoded random suffix, separated from the address by a dot. The ``homeserver_uri`` is the URI of the matrix homeserver at which the user registers.
+In Matrix each user has a userid in the form of ``@<userId>:<homeserver_uri>``. ``userId`` is the lowercased Ethereum address of the node, possibly followed by a 4-bytes hex-encoded random suffix, separated from the address by a dot. The ``homeserver_uri`` is the URI of the matrix homeserver at which the user registers.
 
 Each pair of nodes have a private room for communication. Once a message is added to any of these rooms, they are processed in batch by `MatrixTransport._handle_sync_messages`, after decoding and validation these messages are forwarded to `RaidenService.on_messages` and processed by the node.
 
@@ -275,7 +275,7 @@ So, for a specific queue message, e.g. a ``LockedTransfer`` in a specific channe
 Blockchain Communication
 ************************
 
-We communicate with the blockchain through the `JSONRPCClient <https://github.com/raiden-network/raiden/blob/761bedfee2ee326401ad5ec95d55b1ab458a5213/raiden/network/rpc/client.py#L246>`_ class. That class is a wrapper of ``web3.py`` for all blockchain JSON RPC calls and also checks that the underlying ethereum client is compatible with Raiden. Checks things such as that all required interfaces are enabled and that the version is high enough (Byzantium enabled).
+We communicate with the blockchain through the `JSONRPCClient <https://github.com/raiden-network/raiden/blob/761bedfee2ee326401ad5ec95d55b1ab458a5213/raiden/network/rpc/client.py#L246>`_ class. That class is a wrapper of ``web3.py`` for all blockchain JSON RPC calls and also checks that the underlying Ethereum client is compatible with Raiden. Checks things such as that all required interfaces are enabled and that the version is high enough (Byzantium enabled).
 
 We communicate with each smart contract through the notion of a smart contract `proxy <https://github.com/raiden-network/raiden/blob/761bedfee2ee326401ad5ec95d55b1ab458a5213/raiden/network/rpc/smartcontract_proxy.py#L59>`_. Each proxy is a essentially a class exposing the functionality of a specific contract using the ``JSONRPCClient``.
 
diff --git a/docs/other/known-issues.rst b/docs/other/known-issues.rst
index 36a23f64f4..ef9f0050bc 100644
--- a/docs/other/known-issues.rst
+++ b/docs/other/known-issues.rst
@@ -1,6 +1,9 @@
 Known Issues
 ============
 
+Below you find a non-exhaustive list of known issues, which you should be aware of while using the current version of the software. 
+Most of these issues are not Raiden specific, but rather apply to all blockchain applications.
+
 -  **Compromised user system:** If the system of the user is compromised
    and accessed by an attacker or if a malicious application is running,
    then the write-ahead logging (WAL) could be accessed and valuable
@@ -24,3 +27,27 @@ Known Issues
    processing transactions, which are assumed to be valid only after a
    confirmation period of 5 blocks. This results in 15 blocks wait time
    for opening a channel (three on-chain transactions).
+
+
+**Database Upgrades**
+---------------------
+
+The database layout can change between versions. For **patch** and **minor**
+change releases, Raiden will do automatic upgrades in the background, as soon as
+you start the client with the new version. Your old database will always be kept
+as a backup.
+
+However, for **major** change releases, this may not always be possible. If the
+client tells you, that the database migration was not possible, you are left with
+two options:
+
+- to recover all allocated funds, you should run the previous version of Raiden and
+  **close** and **settle** all channels. Afterwards you can move your old database directory
+  out of the way and join the network again with the same Ethereum keystore file.
+
+- if there are no funds worth recovering, you can also simply start over with a new
+  Ethereum keystore file and
+  `start over <https://raiden-network.readthedocs.io/en/latest/overview_and_guide.html#firing-it-up>`__.
+  This should be true for all usage on testnet and in cases where your channel balances
+  are very low on mainnet. Don't hesitate to
+  `ask for help <https://gitter.im/raiden-network/raiden>`__!
diff --git a/docs/other/safe-usage.rst b/docs/other/safe-usage.rst
index ecd5ffc643..ed14601804 100644
--- a/docs/other/safe-usage.rst
+++ b/docs/other/safe-usage.rst
@@ -15,7 +15,7 @@ Safe Usage
 
 -  **Ethereum node in sync and running reliably:** Ensure that layer 1
    works reliably. This means that you have to have an Ethereum node,
-   either geth or parity, that is synced and working reliably. If there
+   either Geth or OpenEthereum, that is synced and working reliably. If there
    are any problems or bugs on the client then Raiden can not work
    reliably.
 -  **Ethereum client always online:** Make sure that your Ethereum
diff --git a/docs/overview_and_guide.rst b/docs/overview_and_guide.rst
index f097089d7a..cfe86b6528 100644
--- a/docs/overview_and_guide.rst
+++ b/docs/overview_and_guide.rst
@@ -3,34 +3,39 @@ System Requirements and Installation Guide
 .. toctree::
   :maxdepth: 2
 
-Introduction
-============
-Raiden is a payment channel implementation which provides scalable, low latency, and cheap token payments for Ethereum.
-
-.. _binary_releases:
-
 Installation
 ============
 
 To install Raiden you can either:
 
-    * Use the Raiden Wizard
-    * Download a self contained application bundle from the `GitHub release page <https://github.com/raiden-network/raiden/releases>`_
-    * Use pip or, on macOS, homebrew
-    * Run a raiden docker image
+    * :ref:`Use the Raiden Wizard (Quick Start) <wizard_overview>`
+    * :ref:`Download a self-contained application bundle from the GitHub release page  or, on macOS, use homebrew <installation_github>`
+    * :ref:`Use pip<installation_pip>`
+    * :ref:`Run a raiden docker image<installation_docker>`
 
-Below we will give details on how to use the self contained application bundles on different platforms, as well as the other installation methods.
+Below we will give details on how to use the self-contained application bundles on different platforms, as well as the other installation methods.
 
 Installation via Raiden Wizard
 ******************************
 
 If you are new to Raiden or just want to use it without caring too much for the technical details, we recommend the Raiden Wizard to get started.
+It will help you to install Raiden and to acquire all necessary tokens for running it on the Ethereum mainnet.
 
 .. toctree::
-  :maxdepth: 2
+  :maxdepth: 1
 
   installation/quick-start/README
 
+If you want to try out Raiden with test tokens on a testnet before running it on mainnet, 
+you can use the Görli Wizard for installing Raiden and receiving tokens:
+
+.. toctree::
+  :maxdepth: 1
+
+  installation/quick-start-goerli/quick-start-on-goerli-testnet
+
+.. _installation_github:
+
 Installation from GitHub
 ************************
 
@@ -78,6 +83,7 @@ dependencies.
 An Ethereum client is required in both cases. The Raiden binary takes the same command line
 arguments as the ``raiden`` script.
 
+.. _installation_pip:
 
 Installation using pip
 **********************
@@ -90,6 +96,8 @@ If you'd like to give the pre-releases a spin, use pip's `--pre` flag::
 
     pip install --pre raiden
 
+.. _installation_docker:
+
 Installation via Docker
 ***********************
 
@@ -110,8 +118,8 @@ Dependencies
 ************
 You will need a local or remote Ethereum node to connect Raiden to.
 
-- Check `this link <https://github.com/ethereum/go-ethereum/wiki/Building-Ethereum>`_ to install the go-ethereum client.
-- Follow `these instructions <https://github.com/paritytech/parity#simple-one-line-installer-for-mac-and-ubuntu>`_ to install the parity client.
+- Check `this link <https://geth.ethereum.org/docs/install-and-build/installing-geth>`_ to install the Geth client.
+- Download and install OpenEthereum `from here <https://github.com/openethereum/openethereum/releases/latest>`_.
 - Or sign up at a service like `Infura <https://infura.io>`__ to set up a remote node.
 
 Now you are ready :ref:`to get started <running_raiden>`.
@@ -119,18 +127,18 @@ Now you are ready :ref:`to get started <running_raiden>`.
 .. _installation:
 
 For developers
-==============
+**************
 If you plan to develop on the Raiden source code, or the binary distributions do not work for your
 system, you can follow these steps to install a development version.
 
 
 Linux
-*****
+~~~~~
 
 Additional dependencies for development installations
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+-----------------------------------------------------
 
-- You need to make sure that your system has ``solc``, the ethereum solidity compiler installed. Refer to `its documentation`_ for the installation steps.
+- You need to make sure that your system has ``solc``, the Ethereum Solidity compiler installed. Refer to `its documentation`_ for the installation steps.
 - You will also need to obtain the `system dependencies for pyethapp <https://github.com/ethereum/pyethapp/#installation-on-ubuntudebian>`_.
 
 
@@ -139,7 +147,7 @@ Additional dependencies for development installations
 .. _installation_from_source:
 
 Installation from source
-~~~~~~~~~~~~~~~~~~~~~~~~
+------------------------
 
 Clone the repository::
 
@@ -156,40 +164,40 @@ After you have done that you can proceed to install the dependencies::
 
     make install-dev
 
-You will also need to connect your Ethereum client to the Ropsten testnet. See below for guidelines on how to connect with both Parity and Geth.
+You will also need to connect your Ethereum client to the Ropsten testnet. See below for guidelines on how to connect with both OpenEthereum and Geth.
 
 .. _virtualenv: https://docs.python.org/3/library/venv.html
 
 macOS
-*****
+~~~~~
 
 Please refer to the :ref:`detailed step-by-step guide <macos_development_setup>` for setting up a macOS development environment.
 
 nix
-***
+~~~
 
 Please refer to the :ref:`nix setup guide <nix_development_setup>` for setting up a development environment using the `nix <https://nixos.org/nix>`_ package manager.
 
 
 .. _running_raiden:
 
-
-Firing it up
-=============
+Run it
+======
 
 To fire up Raiden you need at least
- 1. a synced **Ethereum Node** - using geth, parity or infura
+ 1. a synced **Ethereum Node** - using Geth, OpenEthereum or Infura
  2. an **Ethereum keystore file** - whereas the address holds ETH, RDN, and the ERC20 token you want to transfer
  3. If you want to use :doc:`Raiden services <raiden_services>` that charge a fee, a deposit of RDN tokens to pay the services with.
 
 More about the Raiden services (pathfinding and monitoring service) will be explained below. On the testnets there are also free services available, and on any network it is possible (though not recommended) to use Raiden without Raiden services.
 
-We will provide you with the necessary cli arguments step by step. Full example is at the end of the page.
+We will provide you with the necessary cli arguments step by step. Full example is at the end of each section.
 
 1. and 2. The synced Ethereum Node & Keystore
 *********************************************
 
-- **Using geth**
+Using Geth
+~~~~~~~~~~
 
 Run the Ethereum client and let it sync::
 
@@ -204,31 +212,33 @@ If problems arise for above method, please see `the Ropsten README <https://gith
 
 Then launch Raiden with the default testnet keystore path::
 
-    raiden --keystore-path  ~/.ethereum/testnet/keystore --pathfinding-service-address $PFS_ADDRESS
+    raiden --keystore-path  ~/.ethereum/testnet/keystore
 
-- **Using parity**
+Using OpenEthereum
+~~~~~~~~~~~~~~~~~~
 
 Run the client and let it sync::
 
-    parity --no-warp --jsonrpc-apis=web3,eth,net,parity
+    openethereum --no-warp --jsonrpc-apis=web3,eth,net,parity
 
 .. note::
     When you want to use a testnet add the ``--chain ropsten`` or ``--chain kovan`` flags or set the network id with ``--network-id`` directly.
 
-.. attention:: Parity sometimes loses its historical DB (potentially after updates). Due to this some events might be lost which will result in Raiden not being able to fetch all events. Therefore it is recommended to make sure to have Parity fully synced with the ``--no-warp`` option.
+.. attention:: OpenEthereum sometimes loses its historical DB (potentially after updates). Due to this some events might be lost which will result in Raiden not being able to fetch all events. Therefore it is recommended to make sure to have OpenEthereum fully synced with the ``--no-warp`` option.
 
-After syncing the chain, an existing Ethereum account can be used or a new one can be generated using ``parity-ethkey``.
+After syncing the chain, an existing Ethereum account can be used or a new one can be generated using `ethkey <https://github.com/openethereum/openethereum/tree/main/crates/accounts/ethkey>`_.
 After account creation, launch Raiden with the path of your keystore supplied::
 
-    raiden --keystore-path ~/.local/share/io.parity.ethereum/keys/test --pathfinding-service-address $PFS_ADDRESS
+    raiden --keystore-path ~/.local/share/openethereum/keys/ethereum
 
 .. _using_rpc-endpoint:
 
-- **Using Infura**
+Using Infura
+~~~~~~~~~~~~
 
 Sign up with `Infura <https://infura.io/>`__ to get an API token. After that you can start using Raiden directly::
 
-    raiden --keystore-path  ~/.ethereum/testnet/keystore --eth-rpc-endpoint "https://<network>.infura.io/v3/<yourToken>"
+    raiden --keystore-path  ~/.ethereum/keystore --eth-rpc-endpoint "https://<network>.infura.io/v3/<yourToken>"
 
 Where `<network>` can be mainnet, ropsten, etc.
 
@@ -237,54 +247,12 @@ Select the desired Ethereum account when prompted, and type in the account's pas
 3. Depositing tokens to pay the services
 ****************************************
 
-To pay the services, you have to lock some of your Raiden tokens in the ``UserDeposit`` contract.
-Normally the Raiden Wizard or some other auxilliary scripts would do the contract interaction for you.
-In this section, we will briefly explain how it can be done manually so you are able to use Raiden without them.
-
-All services that are registered in the service registry of a given network will use one shared instance of ``UserDeposit``.
-You can obtain the address of the contract from
-
-    ``https://github.com/raiden-network/raiden-contracts/blob/master/raiden_contracts/data/deployment_services_<network>.json``
-
-where ``network`` is one of ``mainnet``, ``ropsten``, ``rinkeby`` or ``goerli``.
-
-As an example, suppose we want to run a Raiden node with address ``0x3040435D7F1012e861f0B0989422a47D1825F120`` on the mainnet
-and deposit 10 Raiden tokens (10**19 REI) for it to use. Here we use `MetaMask <https://metamask.io>`_ to access our wallet
-and the contract interface of `Etherscan <https://etherscan.io>`_ to do the transactions. Of course, you can also use another
-service or your own Ethereum node.
+To pay the :doc:`services <raiden_services>`, you have to lock some of your Raiden tokens in the ``UserDeposit`` contract.
+To deposit, you can use the Raiden API, the Raiden Web Interface or manually call the smart contracts:
 
-We log into MetaMask with our account that holds the Raiden tokens (which should be a different account than the one we use with
-Raiden, as the latter is supposed to be used with Raiden only.) To find the ``UserDeposit`` contract, we take a look at ``deployment_services_mainnet.json``:
-
-.. code-block:: none
-
-    {
-        "contracts_version": null, "chain_id": 1,
-        (...)
-        "UserDeposit": {
-            "address": "0x53Cc1decDD7d452c8844a5f383e23AD479A1f614",
-            (...)
-
-
-We can then look up the contract address on Etherscan, and use Etherscan's "read/write contract" panels to interact with it.
-The Raiden token (RDN) can be searched by name on Etherscan, or we can look up its address in the ``UserDeposit`` contract's
-``token`` property. On the testnets, the token symbol is SVT (service token) rather than RDN and it may not be possible
-to find the token by name, but it can always be found in ``UserDeposit.token``.
-
-
-As usual with ERC-20 tokens, we need to call two contract functions:
-
-.. code-block:: none
-
-    approve(0x53Cc1decDD7d452c8844a5f383e23AD479A1f614, 10000000000000000000)
-
-on the RDN (or SVT) token contract, to allow the ``UserDeposit`` contract to move the 10 RDN, and
-
-.. code-block:: none
-
-    deposit(0x3040435D7F1012e861f0B0989422a47D1825F120, 10000000000000000000)
-
-on the ``UserDeposit`` contract.
+- :ref:`Deposit using the Raiden API <mainnet_tutorial_deposit_udc>`
+- :ref:`Deposit from the Raiden Web Interface <webui_udc>`
+- :ref:`Deposit by manually calling the contracts <manual_udc_deposit>`
 
 
 Optional CLI arguments
@@ -296,6 +264,11 @@ There are further CLI arguments with which you can control, among other things
  2. The choice of a monitoring service
  3. Logging
 
+In doubt, you can use the following to see all possible CLI arguments::
+
+    raiden --help
+
+
 1. Pathfinding service
 **********************
 
@@ -306,35 +279,21 @@ If you want to stop broadcasting information about your channel states to
 PFSes, use ``--routing-mode private``. As a result, PFSes won't create routes
 that include your node as a mediator.
 
-If you want to use a particular pathfinding service, e.g. one of the testnet pathfinding services given below, you can
+If you want to use a particular pathfinding service, you can
 do so with ``--pathfinding-service-address <url>``. Otherwise Raiden will automatically pick one of the pathfinding
 services from the registry.
 
 The default setting for the pathfinding options is to use a pathfinding service and choose it automatically
 (``--routing-mode pfs --pathfinding-service-address auto``).
 
-There are pathfinding services running on every testnet at the moment, some that charge fees and some that are for free.
-
-+------------+----------------------------------------------------------+-------------------------------------------------+
-| Testnet    | pfs with fees                                            | pfs without fees                                |
-+============+==========================================================+=================================================+
-| Görli      | https://pfs-goerli-with-fee.services-dev.raiden.network  | https://pfs-goerli.services-dev.raiden.network  |
-+------------+----------------------------------------------------------+-------------------------------------------------+
-| Ropsten    | https://pfs-ropsten-with-fee.services-dev.raiden.network | https://pfs-ropsten.services-dev.raiden.network |
-+------------+----------------------------------------------------------+-------------------------------------------------+
-| Kovan      | https://pfs-kovan-with-fee.services-dev.raiden.network   | https://pfs-kovan.services-dev.raiden.network   |
-+------------+----------------------------------------------------------+-------------------------------------------------+
-| Rinkeby    | https://pfs-rinkeby-with-fee.services-dev.raiden.network | https://pfs-rinkeby.services-dev.raiden.network |
-+------------+----------------------------------------------------------+-------------------------------------------------+
-
-For the mainnet we don't want to prefer and suggest specific pathfinding services.
-
 2. Monitoring service
 *********************
 
 A monitoring service watches a client's open channels while it is offline, and represents the client in case of settlement.
 Like the pathfinding service, it is paid in RDN tokens. If you want to use a monitoring service, use the option
 ``--enable-monitoring`` and Raiden will automatically pick one from its service registry.
+By default the monitoring services are disabled. 
+Enabling monitoring of channels will require a default reward value of 5 RDN for successfully monitoring your channel.
 
 3. Logging configuration
 ************************
diff --git a/docs/raiden-api-1/api-tutorial/1-join-a-token-network.inc.rst b/docs/raiden-api-1/api-tutorial/1-join-a-token-network.inc.rst
index cf33a0c4d2..f4b930dfa6 100644
--- a/docs/raiden-api-1/api-tutorial/1-join-a-token-network.inc.rst
+++ b/docs/raiden-api-1/api-tutorial/1-join-a-token-network.inc.rst
@@ -19,6 +19,8 @@ that token network.
 
 If the address is not in the list you'll have to register the token before you can open a channel.
 
+.. _api_tut_register_token:
+
 Register a new token
 --------------------
 
diff --git a/docs/raiden-api-1/api-tutorial/2-open-a-channel.inc.rst b/docs/raiden-api-1/api-tutorial/2-open-a-channel.inc.rst
index c34ede4328..4fb934ab57 100644
--- a/docs/raiden-api-1/api-tutorial/2-open-a-channel.inc.rst
+++ b/docs/raiden-api-1/api-tutorial/2-open-a-channel.inc.rst
@@ -102,9 +102,7 @@ As you can tell by the response object a channel identifier has been
 generated. This means that there now is a channel with that identifier
 inside the token network.
 
-You're now ready to start making payments.
-
-.. include:: 3-make-a-payment.rst
+You're now ready to start :ref:`making payments <api_tut_make_payment>`.
 
 .. note:: Opening a channel with a partner node is not dependent on whether the partner node holds tokens or not. It will work either way. 
 
diff --git a/docs/raiden-api-1/api-tutorial/README.rst b/docs/raiden-api-1/api-tutorial/README.rst
index 7cd1948c37..c29917e72c 100644
--- a/docs/raiden-api-1/api-tutorial/README.rst
+++ b/docs/raiden-api-1/api-tutorial/README.rst
@@ -1,6 +1,8 @@
 API Tutorial
 ############
 
+This tutorial will show you how to use Raiden via the API by walking you through a few API calls. 
+The steps are similar to the WebUI tutorial, but directly use the API endpoints.
 We will outline the steps necessary for participating in a token
 network. To see all available endpoints visit the
 :ref:`resources <api_endpoints>` part of the documentation.
@@ -23,11 +25,8 @@ This gives us an opportunity to introduce the
 Your Raiden node is up and running if the response returns the same
 address as the Ethereum address used for starting Raiden.
 
-Overview
-========
-
 You're now ready to start interacting with the different endpoints and
-learn how they can be used to:
+learn how they can be used.
 
 .. include:: 1-join-a-token-network.inc.rst
 
diff --git a/docs/rest_api.rst b/docs/rest_api.rst
index f12e2df43a..81f39c0681 100644
--- a/docs/rest_api.rst
+++ b/docs/rest_api.rst
@@ -6,8 +6,11 @@ Raiden's API Documentation
 
 Introduction
 *************
-Raiden has a Restful API with URL endpoints corresponding to user-facing interaction allowed by a Raiden node. The endpoints accept and return JSON encoded objects. The api url path always contains the api version in order to differentiate queries to
-different API versions. All queries start with: ``/api/<version>/``
+The Raiden API is organized around REST and has resource-oriented URL endpoints that accept and return JSON-encoded responses. 
+The Raiden API uses standard HTTP response codes and verbs.
+The Raiden RESTful API endpoints correspond to the interactions allowed by a Raiden node. 
+The URL path always contains the API version as an integer. 
+All endpoints start with ``/api/<version>/``
 
 
 
@@ -27,6 +30,7 @@ Channel Object
        "token_address": "0xEA674fdDe714fd979de3EdF0F56AA9716B898ec8",
        "balance": "25000000",
        "total_deposit": "35000000",
+       "total_withdraw": "15000000",
        "state": "opened",
        "settle_timeout": "500",
        "reveal_timeout": "50"
@@ -36,37 +40,34 @@ Channel Object
 
 A channel object consists of a
 
-- ``channel_identifier`` should be an ``integer`` containing the identifier of the
+- ``channel_identifier``: string containing the identifier of the
   channel.
 
-- ``partner_address`` should be a ``string`` containing the EIP55-encoded address of the
+- ``partner_address``: string containing the EIP55-encoded address of the
   partner with whom we have opened a channel.
 
-- ``token_address`` should be a ``string`` containing the EIP55-encoded address of the
+- ``token_address``: string containing the EIP55-encoded address of the
   token we are trading in the channel.
 
-- ``token_network_address`` should be a ``string`` containing the EIP55-encoded address of the
+- ``token_network_address``: string containing the EIP55-encoded address of the
   token network the channel is part of.
 
-- ``balance`` should be an integer of the amount of the ``token_address`` token we have available for payments.
+- ``balance``: string of the amount of the ``token_address`` token we have available for payments.
 
-- ``total_deposit`` should be an integer of the amount of the ``token_address`` token we have deposited into the contract for this channel.
+- ``total_deposit``: string of the amount of the ``token_address`` token we have deposited into the contract for this channel.
 
-- ``state`` should be the current state of the channel represented by a string.
-  Possible value are:
-  - ``'opened'``: The channel is open and tokens are tradeable
-  - ``'closed'``: The channel has been closed by a participant
-  - ``'settled'``: The channel has been closed by a participant and also settled.
+- ``total_withdraw``: string of the amount of the ``token_address`` token we have withdrawn from the channel on-chain.
 
-- ``'settle_timeout'``: The number of blocks that are required to be mined from the time that ``close()`` is called until the channel can be settled with a call to ``settle()``.
+- ``state``: current state of the channel represented by a string. Possible values are:
 
-- ``'reveal_timeout'``: The maximum number of blocks allowed between the setting of a hashlock and the revealing of the related secret.
+   - ``"opened"``: The channel is open and tokens are tradeable
+   - ``"closed"``: The channel has been closed by a participant
+   - ``"settled"``: The channel has been closed by a participant and also settled.
 
-Event Object
-==============
+- ``settle_timeout``: The number of blocks that are required to be mined from the time that ``close()`` is called until the channel can be settled with a call to ``settle()``.
+
+- ``reveal_timeout``: The maximum number of blocks allowed between the setting of a hashlock and the revealing of the related secret.
 
-Channel events are encoded as json objects with the event arguments as attributes
-of the dictionary, with one difference. The ``event_type`` and the ``block_number`` are also added for all events to easily distinguish between events.
 
 Errors
 ======
@@ -78,19 +79,21 @@ In any way, we consider :http:statuscode:`500` errors as bugs in the Raiden clie
 
 .. _api_endpoints:
 
-Endpoints
-***********
-
-Following are the available API endpoints with which you can interact with Raiden.
+Resources
+*********
 
-Querying Information About Your Raiden Node
-===============================================
+All objects that are sent and received from the Raiden API are JSON encoded.
+The following outlines each of the Raiden API endpoints.
 
 .. _api_address:
 
+Address
+=======
+
 .. http:get:: /api/(version)/address
 
-   Query your address. When raiden starts, you choose an ethereum address which will also be your raiden address.
+   Queries the Ethereum address you choose when starting Raiden. 
+   A Raiden node is up and running correctly if the response returns that same address.
 
    **Example Request**:
 
@@ -110,9 +113,13 @@ Querying Information About Your Raiden Node
           "our_address": "0x2a65Aca4D5fC5B5C859090a6c34d164135398226"
       }
 
+
+Version
+=======
+
 .. http:get:: /api/(version)/version
 
-   Query the version of the Raiden instance
+   You can query the version endpoint to see which version of Raiden you're currently running.
 
    **Example Request**:
 
@@ -132,11 +139,53 @@ Querying Information About Your Raiden Node
           "version": "0.100.5a1.dev157+geb2af878d"
       }
 
+
+Status
+======
+
+.. http:get:: /api/(version)/status
+
+   Query the node status. Possible answers are:
+
+   - ``"ready"``: The node is listening on its API endpoints.
+
+   - ``"syncing"``: The node is still in the initial sync. Number of blocks to sync will also be given.
+
+   - ``"unavailable"``: The node is unavailable for some other reason.
+
+   **Example Request**:
+
+   .. http:example:: curl wget httpie python-requests
+
+      GET /api/v1/status HTTP/1.1
+      Host: localhost:5001
+
+   **Example Response**:
+
+   .. sourcecode:: http
+
+      HTTP/1.1 200 OK
+      Content-Type: application/json
+
+      {
+         "status": "syncing",
+         "blocks_to_sync": "130452"
+      }
+
+   :statuscode 200: Successful query
+   :statuscode 500: Internal Raiden error
+
+
 .. _api_settings:
 
+Settings
+========
+
 .. http:get:: /api/(version)/settings
 
-   Queries the settings of your Raiden node. At the moment only the URL of the pathfinding service is returned. The endpoint will provide more settings in the future.
+   Queries the settings of your Raiden node. 
+   At the moment only the URL of the pathfinding service is returned. 
+   The endpoint will provide more settings in the future.
 
    **Example Request**:
 
@@ -156,10 +205,144 @@ Querying Information About Your Raiden Node
           "pathfinding_service_address": "https://pfs.transport04.raiden.network"
       }
 
-Deploying
+
+Contracts
 =========
+
+.. http:get:: /api/(version)/contracts
+
+   By querying the contracts endpoint you can check which on-chain smart contracts are used.
+   Returns the addresses of the smart contracts in use.
+
+   **Example Request**:
+
+   .. http:example:: curl wget httpie python-requests
+
+      GET /api/v1/contracts HTTP/1.1
+      Host: localhost:5001
+
+   **Example Response**:
+
+   .. sourcecode:: http
+
+      HTTP/1.1 200 OK
+      Content-Type: application/json
+
+      {
+         "contracts_version": "0.37.1",
+         "monitoring_service_address": "0x20e8e5181000e60799A523a2023d630868f378Fd",
+         "one_to_n_address": "0xA514Da2418576CeC4070C82996f30532dDa99706",
+         "secret_registry_address": "0x9fC80eb1939d8147aB90BAC01AD585f3a71BeE7e",
+         "service_registry_address": "0x3bc9C8d34f5714327095358668fD436D7c457C6C",
+         "token_network_registry_address": "0x5a5CF4A63022F61F1506D1A2398490c2e8dfbb98",
+         "user_deposit_address": "0x0794F09913AA8C77C8c5bdd1Ec4Bb51759Ee0cC5"
+      }
+
+
+.. _api_tokens:
+
+Tokens
+======
+
+The tokens endpoints are used for registering new tokens and querying information about already registered tokens.
+
 .. note::
-   For the Raiden Red Eyes release, it will not be possible to register more than one token, due to security reasons in order to minimise possible loss of funds in the case of bugs. The one token that will be registered is `W-ETH <https://weth.io/>`_.
+   For the Alderaan release two tokens are registered, DAI and WETH.
+
+**Information about Tokens**
+
+.. http:get:: /api/(version)/tokens
+
+   Returns a list of addresses of all registered tokens.
+
+   **Example Request**:
+
+   .. http:example:: curl wget httpie python-requests
+
+      GET /api/v1/tokens HTTP/1.1
+      Host: localhost:5001
+
+   **Example Response**:
+
+   .. sourcecode:: http
+
+      HTTP/1.1 200 OK
+      Content-Type: application/json
+
+      [
+            "0xEA674fdDe714fd979de3EdF0F56AA9716B898ec8",
+            "0x61bB630D3B2e8eda0FC1d50F9f958eC02e3969F6"
+      ]
+
+   :statuscode 200: Successful query
+   :statuscode 500: Internal Raiden node error
+   :statuscode 503: The API is currently unavailable, e. g. because the Raiden node is still in the initial sync or shutting down.
+
+.. http:get:: /api/(version)/tokens/(token_address)
+
+   Returns the address of the corresponding token network for the given token, if the token is registered.
+
+   **Example Request**:
+
+   .. http:example:: curl wget httpie python-requests
+
+      GET /api/v1/tokens/0xEA674fdDe714fd979de3EdF0F56AA9716B898ec8 HTTP/1.1
+      Host: localhost:5001
+
+   **Example Response**:
+
+   .. sourcecode:: http
+
+      HTTP/1.1 200 OK
+      Content-Type: application/json
+
+      "0x61bB630D3B2e8eda0FC1d50F9f958eC02e3969F6"
+
+   :statuscode 200: Successful query
+   :statuscode 404: No token network found for the provided token address
+   :statuscode 503: The API is currently unavailable, e. g. because the Raiden node is still in the initial sync or shutting down.
+
+.. http:get:: /api/(version)/tokens/(token_address)/partners
+
+   Returns a list of all partner nodes with unsettled channels for a specific token.
+
+   **Example Request**:
+
+   .. http:example:: curl wget httpie python-requests
+
+      GET /api/v1/tokens/0x61bB630D3B2e8eda0FC1d50F9f958eC02e3969F6/partners HTTP/1.1
+      Host: localhost:5001
+
+   **Example Response**:
+
+   .. sourcecode:: http
+
+      HTTP/1.1 200 OK
+      Content-Type: application/json
+
+      [
+         {
+               "partner_address": "0x2a65aca4d5fc5b5c859090a6c34d164135398226",
+               "channel": "/api/<version>/channels/0x61C808D82A3Ac53231750daDc13c777b59310bD9/0x2a65aca4d5fc5b5c859090a6c34d164135398226"
+         }
+      ]
+
+   :statuscode 200: Successful query
+   :statuscode 302: If the user accesses the channel link endpoint
+   :statuscode 404:
+      - The token does not exist
+      - The token address is not a valid EIP55-encoded Ethereum address
+   :statuscode 500: Internal Raiden node error
+   :statuscode 503: The API is currently unavailable, e. g. because the Raiden node is still in the initial sync or shutting down.
+   :resjsonarr address partner_address: The partner we have a channel with
+   :resjsonarr link channel: A link to the channel resource
+   
+
+**Register a Token**
+
+.. warning::
+   For the Alderaan release it is not be possible to register more than two tokens, due to security reasons in order to minimise possible loss of funds in the case of bugs. 
+   The two token that are registered are DAI and WETH.
 
 .. http:put:: /api/(version)/tokens/(token_address)
 
@@ -184,7 +367,7 @@ Deploying
       }
 
    :statuscode 201: A token network for the token has been successfully created.
-   :statuscode 402: Insufficient ETH to pay for the gas of the register on-chain transaction
+   :statuscode 402: Insufficient ETH to pay for the gas of the register on-chain transaction.
    :statuscode 403: Maximum of allowed token networks reached. No new token networks can be registered.
    :statuscode 404: The given token address is invalid.
    :statuscode 409:
@@ -194,8 +377,21 @@ Deploying
    :statuscode 503: The API is currently unavailable, e. g. because the Raiden node is still in the initial sync or shutting down.
    :resjson address token_network_address: The deployed token networks address.
 
-Querying Information About Channels and Tokens
-==============================================
+
+Channels
+========
+
+The channels endpoints allow you to open channels with other Raiden nodes as well as closing channels, querying them for information and making deposits or withdrawals.
+
+.. warning::
+   The maximum deposits per token and node for the Alderaan release are:
+
+   **DAI**: The deposit limit is 1000 worth of DAI per channel participant making the maximum amount of DAI 2000 per channel.
+   
+   **WETH**: The deposit limit is 4.683 worth of WETH per channel participant making the maximum amount of WETH 9.366 per channel.
+
+
+**Information about Channels**
 
 .. http:get:: /api/(version)/channels
 
@@ -268,7 +464,7 @@ Querying Information About Channels and Tokens
       ]
 
    :statuscode 200: Successful query
-   :statuscode 404: The given token address is not a valid eip55-encoded Ethereum address
+   :statuscode 404: The given token address is not a valid EIP55-encoded Ethereum address
    :statuscode 500: Internal Raiden node error
    :statuscode 503: The API is currently unavailable, e. g. because the Raiden node is still in the initial sync or shutting down.
 
@@ -276,7 +472,8 @@ Querying Information About Channels and Tokens
 
 .. http:get:: /api/(version)/channels/(token_address)/(partner_address)
 
-   Query information about one of your channels. The channel is specified by the address of the token and the partner's address.
+   Query information about one of your channels. 
+   The channel is specified by the address of a token and the address of the partner node which the channel is opened with.
 
    **Example Request**:
 
@@ -307,135 +504,139 @@ Querying Information About Channels and Tokens
 
    :statuscode 200: Successful query
    :statuscode 404:
-    - The given token and / or partner addresses are not valid eip55-encoded Ethereum addresses, or
+    - The given token and / or partner addresses are not valid EIP55-encoded Ethereum addresses, or
     - The channel does not exist
    :statuscode 500: Internal Raiden node error
    :statuscode 503: The API is currently unavailable, e. g. because the Raiden node is still in the initial sync or shutting down.
 
-.. _api_tokens:
 
-.. http:get:: /api/(version)/tokens
+.. _api_open_channel:
 
-   Returns a list of addresses of all registered tokens.
+**Create a Channel**
+
+.. http:put:: /api/(version)/channels
+
+   The request will open a channel and return the newly created channel object.
 
    **Example Request**:
 
    .. http:example:: curl wget httpie python-requests
 
-      GET /api/v1/tokens HTTP/1.1
+      PUT /api/v1/channels HTTP/1.1
       Host: localhost:5001
+      Content-Type: application/json
+
+      {
+          "partner_address": "0x61C808D82A3Ac53231750daDc13c777b59310bD9",
+          "token_address": "0xEA674fdDe714fd979de3EdF0F56AA9716B898ec8",
+          "total_deposit": "35000000",
+          "settle_timeout": "500",
+          "reveal_timeout": "50"
+      }
+
+   :reqjson address partner_address: Address of the partner node with whom we're opening the channel.
+   :reqjson address token_address: Address of the token to be used in the channel.
+   :reqjson string total_deposit: Amount of tokens to be deposited into the channel.
+   :reqjson string settle_timeout: The number of blocks after which a channel can be settled.
+   :reqjson string reveal_timeout: The number of blocks that are allowed between setting a hashlock and the revealing of the related secret.
+
 
    **Example Response**:
 
    .. sourcecode:: http
 
-      HTTP/1.1 200 OK
+      HTTP/1.1 201 CREATED
       Content-Type: application/json
 
-      [
-          "0xEA674fdDe714fd979de3EdF0F56AA9716B898ec8",
-          "0x61bB630D3B2e8eda0FC1d50F9f958eC02e3969F6"
-      ]
+      {
+          "token_network_address": "0xE5637F0103794C7e05469A9964E4563089a5E6f2",
+          "channel_identifier": "20",
+          "partner_address": "0x61C808D82A3Ac53231750daDc13c777b59310bD9",
+          "token_address": "0xEA674fdDe714fd979de3EdF0F56AA9716B898ec8",
+          "balance": "25000000",
+          "total_deposit": "35000000",
+          "total_withdraw": "0",
+          "state": "opened",
+          "settle_timeout": "500",
+          "reveal_timeout": "50"
+      }
 
-   :statuscode 200: Successful query
+   :statuscode 201: Channel created successfully
+   :statuscode 400: Provided JSON is in some way malformed
+   :statuscode 402: Insufficient ETH to pay for the gas of the channel open on-chain transaction
+   :statuscode 409: Invalid input, e. g. too low a settle timeout
    :statuscode 500: Internal Raiden node error
    :statuscode 503: The API is currently unavailable, e. g. because the Raiden node is still in the initial sync or shutting down.
 
-.. http:get:: /api/(version)/tokens/(token_address)
 
-   Returns the address of the corresponding token network for the given token, if the token is registered.
+**Modify a Channel**
 
-   **Example Request**:
+.. http:patch:: /api/(version)/channels/(token_address)/(partner_address)
 
-   .. http:example:: curl wget httpie python-requests
+   This request is used to close a channel, to increase the deposit in it, to withdraw tokens from it or to update its reveal timeout.
+   The channel is specified by the address of a token and the address of the partner node which the channel is opened with.
 
-      GET /api/v1/tokens/0xEA674fdDe714fd979de3EdF0F56AA9716B898ec8 HTTP/1.1
-      Host: localhost:5001
+   .. _api_close_channel:
 
-   **Example Response**:
+   **Close Channel Example Request**:
 
-   .. sourcecode:: http
+   .. http:example:: curl wget httpie python-requests
 
-      HTTP/1.1 200 OK
+      PATCH /api/v1/channels/0xEA674fdDe714fd979de3EdF0F56AA9716B898ec8/0x61C808D82A3Ac53231750daDc13c777b59310bD9 HTTP/1.1
+      Host: localhost:5001
       Content-Type: application/json
 
-      "0x61bB630D3B2e8eda0FC1d50F9f958eC02e3969F6"
-
-   :statuscode 200: Successful query
-   :statuscode 404: No token network found for the provided token address
-   :statuscode 503: The API is currently unavailable, e. g. because the Raiden node is still in the initial sync or shutting down.
+      {
+          "state": "closed"
+      }
 
-.. http:get:: /api/(version)/tokens/(token_address)/partners
+   :reqjson string state: Can only be set to ``"closed"``
 
-   Returns a list of all partners with whom you have non-settled channels for a certain token.
+   .. _api_increase_deposit:
 
-   **Example Request**:
+   **Increase Deposit Example Request**:
 
    .. http:example:: curl wget httpie python-requests
 
-      GET /api/v1/tokens/0x61bB630D3B2e8eda0FC1d50F9f958eC02e3969F6/partners HTTP/1.1
+      PATCH /api/v1/channels/0xEA674fdDe714fd979de3EdF0F56AA9716B898ec8/0x61C808D82A3Ac53231750daDc13c777b59310bD9 HTTP/1.1
       Host: localhost:5001
-
-   **Example Response**:
-
-   .. sourcecode:: http
-
-      HTTP/1.1 200 OK
       Content-Type: application/json
 
-      [
-         {
-             "partner_address": "0x2a65aca4d5fc5b5c859090a6c34d164135398226",
-             "channel": "/api/<version>/channels/0x61C808D82A3Ac53231750daDc13c777b59310bD9/0x2a65aca4d5fc5b5c859090a6c34d164135398226"
-         }
-      ]
-
-   :statuscode 200: Successful query
-   :statuscode 302: If the user accesses the channel link endpoint
-   :statuscode 404:
-    - The token does not exist
-    - The token address is not a valid eip55-encoded Ethereum address
-   :statuscode 500: Internal Raiden node error
-   :statuscode 503: The API is currently unavailable, e. g. because the Raiden node is still in the initial sync or shutting down.
-   :resjsonarr address partner_address: The partner we have a channel with
-   :resjsonarr link channel: A link to the channel resource
+      {
+          "total_deposit": "100"
+      }
 
-.. http:get:: /api/(version)/pending_transfers
+   :reqjson string total_deposit: The increased total deposit
 
-   Returns a list of all transfers that have not been completed yet.
+   .. _api_withdraw:
 
-   **Example Request**:
+   **Withdraw Tokens Example Request**:
 
    .. http:example:: curl wget httpie python-requests
 
-      GET /api/v1/pending_transfers HTTP/1.1
+      PATCH /api/v1/channels/0xEA674fdDe714fd979de3EdF0F56AA9716B898ec8/0x61C808D82A3Ac53231750daDc13c777b59310bD9 HTTP/1.1
       Host: localhost:5001
+      Content-Type: application/json
 
-   See below for an example response.
-
-.. http:get:: /api/(version)/pending_transfers/(token_address)
+      {
+          "total_withdraw": "100"
+      }
 
-   Like above, but limited to pending transfers of the specified token.
+   :reqjson string total_withdraw: The increased total withdraw
 
-   **Example Request**:
+   **Update Reveal Timeout Example Request**:
 
    .. http:example:: curl wget httpie python-requests
 
-      GET /api/v1/pending_transfers/0xd0A1E359811322d97991E03f863a0C30C2cF029C HTTP/1.1
+      PATCH /api/v1/channels/0xEA674fdDe714fd979de3EdF0F56AA9716B898ec8/0x61C808D82A3Ac53231750daDc13c777b59310bD9 HTTP/1.1
       Host: localhost:5001
+      Content-Type: application/json
 
-   See below for an example response.
-
-.. http:get:: /api/(version)/pending_transfers/(token_address)/(partner_address)
-
-   Like above, but limited to the specified channel.
-
-   **Example Request**:
-
-   .. http:example:: curl wget httpie python-requests
+      {
+          "reveal_timeout": "50"
+      }
 
-      GET /api/v1/pending_transfers/0xd0A1E359811322d97991E03f863a0C30C2cF029C/0x2c4b0Bdac486d492E3cD701F4cA87e480AE4C685 HTTP/1.1
-      Host: localhost:5001
+   :reqjson string reveal_timeout: The new reveal timeout value
 
    **Example Response**:
 
@@ -444,163 +645,212 @@ Querying Information About Channels and Tokens
       HTTP/1.1 200 OK
       Content-Type: application/json
 
-      [
-         {
-            "channel_identifier": "255",
-            "initiator": "0x5E1a3601538f94c9e6D2B40F7589030ac5885FE7",
-            "locked_amount": "119",
-            "payment_identifier": "1",
-            "role": "initiator",
-            "target": "0x00AF5cBfc8dC76cd599aF623E60F763228906F3E",
-            "token_address": "0xd0A1E359811322d97991E03f863a0C30C2cF029C",
-            "token_network_address": "0x111157460c0F41EfD9107239B7864c062aA8B978",
-            "transferred_amount": "331"
-         }
-      ]
+      {
+          "token_network_address": "0xE5637F0103794C7e05469A9964E4563089a5E6f2",
+          "channel_identifier": "20",
+          "partner_address": "0x61C808D82A3Ac53231750daDc13c777b59310bD9",
+          "token_address": "0xEA674fdDe714fd979de3EdF0F56AA9716B898ec8",
+          "balance": "25000000",
+          "total_deposit": "35000000",
+          "total_withdraw": "5000000",
+          "state": "closed",
+          "settle_timeout": "500",
+          "reveal_timeout": "50"
+      }
 
-   :statuscode 200: Successful query
-   :statuscode 404: The queried channel or token was not found
+   :statuscode 200: Success
+   :statuscode 400:
+    - The provided JSON is in some way malformed, or
+    - there is nothing to do since none of ``state``, ``total_deposit`` or ``total_withdraw`` have been given, or
+    - the value of ``state`` is not a valid channel state.
+   :statuscode 402: Insufficient balance to do a deposit, or insufficient ETH to pay for the gas of the on-chain transaction
+   :statuscode 404: The given token and / or partner addresses are not valid EIP55-encoded Ethereum addresses
+   :statuscode 409:
+    - Provided channel does not exist or
+    - ``state``, ``total_deposit`` and ``total_withdraw`` have been attempted to update in the same request or
+    - attempt to deposit token amount lower than on-chain balance of the channel or
+    - attempt to deposit more tokens than the testing limit
    :statuscode 500: Internal Raiden node error
    :statuscode 503: The API is currently unavailable, e. g. because the Raiden node is still in the initial sync or shutting down.
-   :resjsonarr string role: One of "initiator", "mediator" and "target"
 
 
+.. _Payments:
 
+Payments
+========
 
-Channel Management
-==================
+The payment endpoint is used for transferring tokens to another node. 
+You can send the desired amount of tokens by providing the address of the token and the address of the receiving node.
+Besides you can query all payments that you sent or received.
 
-.. _api_open_channel:
 
-.. http:put:: /api/(version)/channels
-
-   Opens (i. e. creates) a channel.
+**Query the Payment History**
 
-   **Example Request**:
+.. _api_list_payments:
 
-   .. http:example:: curl wget httpie python-requests
+.. http:get:: /api/(version)/payments/(token_address)/(partner_address)
 
-      PUT /api/v1/channels HTTP/1.1
-      Host: localhost:5001
-      Content-Type: application/json
+   When querying the payment history the response will include:
 
-      {
-          "partner_address": "0x61C808D82A3Ac53231750daDc13c777b59310bD9",
-          "token_address": "0xEA674fdDe714fd979de3EdF0F56AA9716B898ec8",
-          "total_deposit": "35000000",
-          "settle_timeout": "500",
-          "reveal_timeout": "50"
-      }
+   * "EventPaymentSentSuccess" for successful payments
+   * "EventPaymentSentFailed" for failed payments
+   * "EventPaymentReceivedSuccess" for received payments
 
-   :reqjson address partner_address: The partner we want to open a channel with.
-   :reqjson address token_address: The token we want to be used in the channel.
-   :reqjson int total_deposit: Total amount of tokens to be deposited to the channel
-   :reqjson int settle_timeout: The amount of blocks that the settle timeout should have.
-   :reqjson int reveal_timeout: The amount of blocks that the reveal timeout should have.
+   ``token_address`` and ``partner_address`` are optional and will filter the list of events accordingly.
 
-   The request's payload is a channel object; since it is a new channel, its ``channel_address``
-   and ``status`` fields will be ignored and can be omitted.
+   **Example Request**:
 
-   The request to the endpoint will later return the fully created channel object.
+   .. http:example:: curl wget httpie python-requests
 
-   .. note::
-      For the Raiden Red Eyes release the maximum deposit per node in a channel is limited to 0.075 worth of `W-ETH <https://weth.io/>`_. This means that the maximum amount of tokens in a channel is limited to 0.15 worth of W-ETH. This is done to mitigate risk since the Red Eyes release is an alpha testing version on the mainnet.
+      GET /api/v1/payments/0x0f114A1E9Db192502E7856309cc899952b3db1ED/0x82641569b2062B545431cF6D7F0A418582865ba7  HTTP/1.1
+      Host: localhost:5001
+   
+   :query int limit: Limits the payment history result to the specified amount 
+   :query int offset: Offsets the payment history result by the specified amount
 
 
    **Example Response**:
 
    .. sourcecode:: http
 
-      HTTP/1.1 201 CREATED
+      HTTP/1.1 200 OK
       Content-Type: application/json
 
-      {
-          "token_network_address": "0xE5637F0103794C7e05469A9964E4563089a5E6f2",
-          "channel_identifier": "20",
-          "partner_address": "0x61C808D82A3Ac53231750daDc13c777b59310bD9",
-          "token_address": "0xEA674fdDe714fd979de3EdF0F56AA9716B898ec8",
-          "balance": "25000000",
-          "total_deposit": "35000000",
-          "total_withdraw": "0",
-          "state": "opened",
-          "settle_timeout": "500",
-          "reveal_timeout": "50"
-      }
+      [
+         {
+            "event": "EventPaymentSentSuccess",
+            "amount": "20",
+            "target": "0x82641569b2062B545431cF6D7F0A418582865ba7",
+            "identifier": "3",
+            "log_time": "2018-10-30T07:10:13.122",
+            "token_address": "0x62083c80353Df771426D209eF578619EE68D5C7A"
+         },
+         {
+            "target": "0x82641569b2062B545431cF6D7F0A418582865ba7",
+            "event": "EventPaymentSentFailed",
+            "log_time": "2018-10-30T07:04:22.293",
+            "reason": "there is no route available",
+            "token_address": "0x62083c80353Df771426D209eF578619EE68D5C7A"
+         },
+         {
+            "event": "EventPaymentReceivedSuccess",
+            "amount": "5",
+            "initiator": "0x82641569b2062B545431cF6D7F0A418582865ba7",
+            "identifier": "1",
+            "log_time": "2018-10-30T07:03:52.193",
+            "token_address": "0x62083c80353Df771426D209eF578619EE68D5C7A"
+         }
+      ]
 
-   :statuscode 201: Channel created successfully
-   :statuscode 400: Provided JSON is in some way malformed
-   :statuscode 402: Insufficient ETH to pay for the gas of the channel open on-chain transaction
-   :statuscode 409: Invalid input, e. g. too low a settle timeout
+   :statuscode 200: For successful query
+   :statuscode 404: The given token and / or partner addresses are not valid EIP55-encoded Ethereum addresses
+   :statuscode 409: If the given block number or token_address arguments are invalid
    :statuscode 500: Internal Raiden node error
    :statuscode 503: The API is currently unavailable, e. g. because the Raiden node is still in the initial sync or shutting down.
+   :resjsonarr string event: One of "EventPaymentSentSuccess", "EventPaymentSentFailed" and "EventPaymentReceivedSuccess".
+   :resjsonarr string amount: Token amount of the payment.
+   :resjsonarr string target: Address of the node which received the payment.
+   :resjsonarr string initiator: Address of the node which initiated the payment.
+   :resjsonarr string identifier: Identifier of the payment.
+   :resjsonarr string log_time: Time when the payment event was written to the write-ahead log. The format of ``log_time`` is ISO8601 with milliseconds.
+   :resjsonarr string token_address: Address of token that was transferred.
+   :resjsonarr string reason: Gives an explanation why a payment failed.
 
-.. _api_close_channel:
 
-.. http:patch:: /api/(version)/channels/(token_address)/(partner_address)
+**Initiate a Payment**
 
-   This request is used to close a channel or to increase the deposit in it.
+.. _api_init_payment:
+
+.. http:post:: /api/(version)/payments/(token_address)/(target_address)
 
-   **Example Request (close channel)**:
+   The request will only return once the payment either succeeds or fails.
+
+   .. note::
+      A payment can fail due to:
+
+      * The secret for opening the hashlock not being revealed in time and the lock expires
+      * The target node being offline
+      * The channels leading to the target node not having enough ``settle_timeout`` and ``reveal_timeout``
+      * The funds not being enough
+
+   
+   **Example Request**:
 
    .. http:example:: curl wget httpie python-requests
 
-      PATCH /api/v1/channels/0xEA674fdDe714fd979de3EdF0F56AA9716B898ec8/0x61C808D82A3Ac53231750daDc13c777b59310bD9 HTTP/1.1
+      POST /api/v1/payments/0x2a65Aca4D5fC5B5C859090a6c34d164135398226/0x61C808D82A3Ac53231750daDc13c777b59310bD9 HTTP/1.1
       Host: localhost:5001
       Content-Type: application/json
 
       {
-          "state": "closed"
+          "amount": "200",
+          "identifier": "42"
       }
 
-.. _api_increase_deposit:
+   :reqjson string amount: Amount to be sent to the target
+   :reqjson string identifier: Identifier of the payment (optional)
+   :reqjson string lock_timeout: lock timeout, in blocks, to be used with the payment. Default is 2 * channel's reveal_timeout, Value must be greater than channel's reveal_timeout (optional)
 
-   **Example Request (increase deposit)**:
 
-   .. http:example:: curl wget httpie python-requests
+   **Example Response**:
 
-      PATCH /api/v1/channels/0xEA674fdDe714fd979de3EdF0F56AA9716B898ec8/0x61C808D82A3Ac53231750daDc13c777b59310bD9 HTTP/1.1
-      Host: localhost:5001
+   .. sourcecode:: http
+
+      HTTP/1.1 200 OK
       Content-Type: application/json
 
       {
-          "total_deposit": "100"
+          "initiator_address": "0xEA674fdDe714fd979de3EdF0F56AA9716B898ec8",
+          "target_address": "0x61C808D82A3Ac53231750daDc13c777b59310bD9",
+          "token_address": "0x2a65Aca4D5fC5B5C859090a6c34d164135398226",
+          "amount": "200",
+          "identifier": "42",
+          "secret": "0x4c7b2eae8bbed5bde529fda2dcb092fddee3cc89c89c8d4c747ec4e570b05f66",
+          "secret_hash": "0x1f67db95d7bf4c8269f69d55831e627005a23bfc199744b7ab9abcb1c12353bd"
       }
 
-.. _api_withdraw:
+   :statuscode 200: Successful payment
+   :statuscode 400: The provided json is in some way malformed
+   :statuscode 402: The payment can't start due to insufficient balance
+   :statuscode 404: The given token and / or target addresses are not valid EIP55-encoded Ethereum addresses
+   :statuscode 409: The address or the amount is invalid, or there is no path to the target, or the identifier is already in use for a different payment.
+   :statuscode 500: Internal Raiden node error
+   :statuscode 503: The API is currently unavailable, e. g. because the Raiden node is still in the initial sync or shutting down.
 
-   **Example Request (withdraw tokens)**:
+   .. note::
+      This endpoint will return as soon the initiator has unlocked the payment(i.e Unlock message is sent).
+      However, this does not necessarily mean that querying the balance from the target node, immediately after
+      the initiator returns, will return the new balance amount due to the fact that the target might not have received or processed the unlock.
+  
 
-   .. http:example:: curl wget httpie python-requests
+.. _api_user_deposit:
 
-      PATCH /api/v1/channels/0xEA674fdDe714fd979de3EdF0F56AA9716B898ec8/0x61C808D82A3Ac53231750daDc13c777b59310bD9 HTTP/1.1
-      Host: localhost:5001
-      Content-Type: application/json
+User Deposit
+============
+
+For paying the :doc:`Raiden Services <raiden_services>` it is necessary to have RDN (Raiden Network Tokens) in the User Deposit Contract (UDC). 
+This endpoint can be used to deposit to and withdraw from the UDC.
 
-      {
-          "total_withdraw": "100"
-      }
 
-   **Example Request (update channel reveal timeout)**:
+**Deposit**
+
+.. http:post:: /api/(version)/user_deposit
+
+   Deposit RDN tokens to the UDC.
+
+   **Example Request**:
 
    .. http:example:: curl wget httpie python-requests
 
-      PATCH /api/v1/channels/0xEA674fdDe714fd979de3EdF0F56AA9716B898ec8/0x61C808D82A3Ac53231750daDc13c777b59310bD9 HTTP/1.1
+      POST /api/v1/user_deposit HTTP/1.1
       Host: localhost:5001
       Content-Type: application/json
 
       {
-          "reveal_timeout": "50"
+          "total_deposit": "200000"
       }
 
-   :reqjson string state: Desired new state; the only valid choice is ``"closed"``
-   :reqjson int total_deposit: The increased total deposit
-   :reqjson int total_withdraw: The increased total withdraw
-   :reqjson int reveal_timeout: The new reveal timeout value
-
-   .. note::
-      For the Raiden Red Eyes release the maximum deposit per node in a channel is limited to 0.075 worth of `W-ETH <https://weth.io/>`_.
-      This means that the maximum amount of tokens in a channel is limited to 0.15 worth of W-ETH.
-      This is done to mitigate risk since the Red Eyes release is an alpha testing version on the mainnet.
+   :reqjson string total_deposit: The total deposit token amount. Should be the sum of the current value and the desired deposit.
 
    **Example Response**:
 
@@ -610,48 +860,37 @@ Channel Management
       Content-Type: application/json
 
       {
-          "token_network_address": "0xE5637F0103794C7e05469A9964E4563089a5E6f2",
-          "channel_identifier": "20",
-          "partner_address": "0x61C808D82A3Ac53231750daDc13c777b59310bD9",
-          "token_address": "0xEA674fdDe714fd979de3EdF0F56AA9716B898ec8",
-          "balance": "25000000",
-          "total_deposit": "35000000",
-          "total_withdraw": "5000000",
-          "state": "closed",
-          "settle_timeout": "500",
-          "reveal_timeout": "50"
+         "transaction_hash": "0xc5988c93c07cf1579e73d39ee2a3d6e948d959d11015465a77817e5239165170"
       }
 
-   :statuscode 200: Success
-   :statuscode 400:
-    - The provided JSON is in some way malformed, or
-    - there is nothing to do since none of ``state``, ``total_deposit`` or ``total_withdraw`` have been given, or
-    - the value of ``state`` is not a valid channel state.
-   :statuscode 402: Insufficient balance to do a deposit, or insufficient ETH to pay for the gas of the on-chain transaction
-   :statuscode 404: The given token and / or partner addresses are not valid eip55-encoded Ethereum addresses
-   :statuscode 409:
-    - Provided channel does not exist or
-    - ``state``, ``total_deposit`` and ``total_withdraw`` have been attempted to update in the same request or
-    - attempt to deposit token amount lower than on-chain balance of the channel
-    - attempt to deposit more tokens than the testing limit
+   :statuscode 200: Deposit was successful
+   :statuscode 400: The provided JSON is in some way malformed
+   :statuscode 402: Insufficient balance to do a deposit or insufficient ETH to pay for the gas of the on-chain transaction
+   :statuscode 404: No UDC is configured on the Raiden node
+   :statuscode 409: The provided ``total_deposit`` is not higher than the previous ``total_deposit`` or attempted to deposit more RDN than the UDC limit would allow
    :statuscode 500: Internal Raiden node error
    :statuscode 503: The API is currently unavailable, e. g. because the Raiden node is still in the initial sync or shutting down.
 
-Connection Management
-=====================
 
-.. http:get:: /api/(version)/connections
+**Plan a withdraw**
 
-   Query details of all joined token networks.
+.. http:post:: /api/(version)/user_deposit
 
-   The request will return a JSON object where each key is a token address for which you have open channels.
+   Before RDN can be withdrawn from the UDC the withdraw must be planned.
 
    **Example Request**:
 
    .. http:example:: curl wget httpie python-requests
 
-      GET /api/v1/connections HTTP/1.1
+      POST /api/v1/user_deposit HTTP/1.1
       Host: localhost:5001
+      Content-Type: application/json
+
+      {
+          "planned_withdraw_amount": "1500"
+      }
+
+   :reqjson string planned_withdraw_amount: The amount of tokens for which a withdrawal should get planned.
 
    **Example Response**:
 
@@ -661,36 +900,38 @@ Connection Management
       Content-Type: application/json
 
       {
-          "0x2a65Aca4D5fC5B5C859090a6c34d164135398226": {
-              "sum_deposits": "67",
-              "channels": "3"
-          },
-          "0x0f114A1E9Db192502E7856309cc899952b3db1ED": {
-              "sum_deposits": "31",
-              "channels": "1"
-          }
+         "planned_withdraw_block_number": 4269933,
+         "transaction_hash": "0xec6a0d010d740df20ca8b3b6456e9deaab8abf3787cb676ee244bef7d28aa4fc"
       }
 
-   :statuscode 200: For a successful query
+   :statuscode 200: Withdraw plan was successful
+   :statuscode 400: The provided JSON is in some way malformed
+   :statuscode 402: Insufficient ETH to pay for the gas of the on-chain transaction
+   :statuscode 404: No UDC is configured on the Raiden node
+   :statuscode 409: The provided ``planned_withdraw_amount`` is higher than the balance in the UDC or not greater than zero
    :statuscode 500: Internal Raiden node error
    :statuscode 503: The API is currently unavailable, e. g. because the Raiden node is still in the initial sync or shutting down.
-   :resjsonarr int sum_deposits: Sum of deposits of all currently open channels
-   :resjsonarr int channels: Number of channels currently open for that token
 
-.. _api_leave_tn:
+**Withdraw**
 
-.. http:delete:: /api/(version)/connections/(token_address)
+.. http:post:: /api/(version)/user_deposit
 
-   Leave a token network. The request will only return once all blockchain calls for closing/settling a channel have completed.
+   Withdraw RDN from the UDC. Can only be done 100 blocks after the withdraw was planned.
 
    **Example Request**:
 
    .. http:example:: curl wget httpie python-requests
 
-      DELETE /api/v1/connections/0x2a65Aca4D5fC5B5C859090a6c34d164135398226 HTTP/1.1
+      POST /api/v1/user_deposit HTTP/1.1
       Host: localhost:5001
       Content-Type: application/json
 
+      {
+          "withdraw_amount": "1500"
+      }
+
+   :reqjson string withdraw_amount: The amount of tokens which should get withdrawn.
+
    **Example Response**:
 
    .. sourcecode:: http
@@ -698,52 +939,36 @@ Connection Management
       HTTP/1.1 200 OK
       Content-Type: application/json
 
-      [
-          "0x41BCBC2fD72a731bcc136Cf6F7442e9C19e9f313",
-          "0x5A5f458F6c1a034930E45dC9a64B99d7def06D7E",
-          "0x8942c06FaA74cEBFf7d55B79F9989AdfC85C6b85"
-      ]
-
-   The response is a list with the addresses of all closed channels.
+      {
+         "transaction_hash": "0xfc7edd195c6cc0c9391d84dd83b7aa9dbfffbcfc107e5c33a5ab912c0d92416c"
+      }
 
-   :statuscode 200: For successfully leaving a token network
-   :statuscode 404: The given token address is not a valid eip55-encoded Ethereum address
+   :statuscode 200: Withdraw was successful
+   :statuscode 400: The provided JSON is in some way malformed
+   :statuscode 402: Insufficient ETH to pay for the gas of the on-chain transaction
+   :statuscode 404: No UDC is configured on the Raiden node
+   :statuscode 409: The provided ``withdraw_amount`` is higher than the planned amount or not greater than zero or the withdraw is too early
    :statuscode 500: Internal Raiden node error
    :statuscode 503: The API is currently unavailable, e. g. because the Raiden node is still in the initial sync or shutting down.
 
-   .. note::
-      Currently, the API calls are blocking. This means that in the case of long running calls like ``leave``, if an API call is currently being processed by Raiden, all pending calls will be queued and processed with their passed API call argument.
 
-.. _Payments:
+Connections
+===========
 
-Payments
-========
+The connections endpoints allow you to query details about all joined token networks as well as leave a token network by closing and settling all open channels.
 
-.. _api_init_payment:
+**Details of All Joined Token Networks**
 
-.. http:post:: /api/(version)/payments/(token_address)/(target_address)
-
-   Initiate a payment.
+.. http:get:: /api/(version)/connections
 
-   The request will only return once the payment either succeeded or failed. A payment can fail due to the expiration of a lock, the target being offline, channels on the path to the target not having enough ``settle_timeout`` and ``reveal_timeout`` in order to allow the payment to be propagated safely, not enough funds etc.
+   The request will return a JSON object where each key is a token address for which you have open channels.
 
    **Example Request**:
 
    .. http:example:: curl wget httpie python-requests
 
-      POST /api/v1/payments/0x2a65Aca4D5fC5B5C859090a6c34d164135398226/0x61C808D82A3Ac53231750daDc13c777b59310bD9 HTTP/1.1
+      GET /api/v1/connections HTTP/1.1
       Host: localhost:5001
-      Content-Type: application/json
-
-      {
-          "amount": "200",
-          "identifier": "42"
-      }
-
-   :reqjson int amount: Amount to be sent to the target
-   :reqjson int identifier: Identifier of the payment (optional)
-   :reqjson int lock_timeout: lock timeout, in blocks, to be used with the payment. Default is 2 * channel's reveal_timeout, Value must be greater than channel's reveal_timeout (optional)
-
 
    **Example Response**:
 
@@ -753,176 +978,104 @@ Payments
       Content-Type: application/json
 
       {
-          "initiator_address": "0xEA674fdDe714fd979de3EdF0F56AA9716B898ec8",
-          "target_address": "0x61C808D82A3Ac53231750daDc13c777b59310bD9",
-          "token_address": "0x2a65Aca4D5fC5B5C859090a6c34d164135398226",
-          "amount": "200",
-          "identifier": "42",
-          "secret": "0x4c7b2eae8bbed5bde529fda2dcb092fddee3cc89c89c8d4c747ec4e570b05f66",
-          "secret_hash": "0x1f67db95d7bf4c8269f69d55831e627005a23bfc199744b7ab9abcb1c12353bd"
+         "0x2a65Aca4D5fC5B5C859090a6c34d164135398226": {
+               "sum_deposits": "67",
+               "channels": "3"
+         },
+         "0x0f114A1E9Db192502E7856309cc899952b3db1ED": {
+               "sum_deposits": "31",
+               "channels": "1"
+         }
       }
 
-   :statuscode 200: Successful payment
-   :statuscode 400: If the provided json is in some way malformed
-   :statuscode 402: If the payment can't start due to insufficient balance
-   :statuscode 404: The given token and / or target addresses are not valid eip55-encoded Ethereum addresses
-   :statuscode 409: If the address or the amount is invalid or if there is no path to the target, or if the identifier is already in use for a different payment.
+   :statuscode 200: For a successful query
    :statuscode 500: Internal Raiden node error
    :statuscode 503: The API is currently unavailable, e. g. because the Raiden node is still in the initial sync or shutting down.
+   :resjsonarr string sum_deposits: Sum of deposits in all currently open channels
+   :resjsonarr string channels: Number of channels currently open for the specific token
 
-   .. note::
-      This endpoint will return as soon the initiator has unlocked the payment(i.e Unlock message is sent).
-      However, this does not necessarily mean that querying the balance from the target node, immediately after
-      the initiator returns, will return the new balance amount due to the fact that the target might not have received or processed the unlock.
-
-   To use Raiden for an atomic swap (see :doc:`Token Swaps <token_swaps>`), the endpoint could be called to initiate a payment while providing values for ``secret`` and ``secret_hash``.
-
-   **Example Request**:
-
-   .. http:example:: curl wget httpie python-requests
-
-      POST /api/v1/payments/0x2a65Aca4D5fC5B5C859090a6c34d164135398226/0x61C808D82A3Ac53231750daDc13c777b59310bD9 HTTP/1.1
-      Host: localhost:5001
-      Content-Type: application/json
-
-      {
-          "amount": "200",
-          "identifier": "42",
-          "secret": "0x4c7b2eae8bbed5bde529fda2dcb092fddee3cc89c89c8d4c747ec4e570b05f66",
-          "secret_hash": "0x1f67db95d7bf4c8269f69d55831e627005a23bfc199744b7ab9abcb1c12353bd"
-      }
-
-   :reqjson int amount: Amount to be sent to the target
-   :reqjson int identifier: Identifier of the payment (optional)
-   :reqjson string secret: The secret to be used for the payment
-   :reqjson string secret_hash: The secret hash (should be equal to SHA256 of the secret)
-
-
-Querying Events
-===============
 
-Events are kept by the node. A normal user should only care about the events exposed for payments. Those events show if a payment failed or if it was successful.
-
-For ``raiden_events`` you can provide a ``limit`` and an ``offset`` number which would define the limit of results to return and the offset from which to return results respectively.
+.. _api_leave_tn:
 
-``raiden_events`` contain a timestamp field, ``log_time``, indicating when they were written to the write-ahead log.
-The format of ``log_time`` is ISO8601 with milliseconds.
+**Leave a Token Network**
 
-.. _api_list_payments:
+.. http:delete:: /api/(version)/connections/(token_address)
 
-.. http:get:: /api/(version)/payments/(token_address)/(target_address)
+   The request might take some time because it will only return once all blockchain calls for closing and settling a channel have been completed.
 
-     Query the payment history. This includes successful (EventPaymentSentSuccess) and failed (EventPaymentSentFailed) sent payments as well as received payments (EventPaymentReceivedSuccess).
-     ``token_address`` and ``target_address`` are optional and will filter the list of events accordingly.
+   The response is a list with the addresses of all closed channels.
 
-    **Example Request**:
+   **Example Request**:
 
-    .. http:example:: curl wget httpie python-requests
+   .. http:example:: curl wget httpie python-requests
 
-       GET /api/v1/payments/0x0f114A1E9Db192502E7856309cc899952b3db1ED/0x82641569b2062B545431cF6D7F0A418582865ba7  HTTP/1.1
-       Host: localhost:5001
+      DELETE /api/v1/connections/0x2a65Aca4D5fC5B5C859090a6c34d164135398226 HTTP/1.1
+      Host: localhost:5001
+      Content-Type: application/json
 
-    **Example Response**:
+   **Example Response**:
 
-    .. sourcecode:: http
+   .. sourcecode:: http
 
       HTTP/1.1 200 OK
       Content-Type: application/json
 
       [
-          {
-              "event": "EventPaymentReceivedSuccess",
-              "amount": "5",
-              "initiator": "0x82641569b2062B545431cF6D7F0A418582865ba7",
-              "identifier": "1",
-              "log_time": "2018-10-30T07:03:52.193",
-	      "token_address" : "0x5a2d2b9b015b46b8eaff7bffdc5db0051db7439b"
-          },
-          {
-              "event": "EventPaymentSentSuccess",
-              "amount": "35",
-              "target": "0x82641569b2062B545431cF6D7F0A418582865ba7",
-              "identifier": "2",
-              "log_time": "2018-10-30T07:04:22.293",
-	      "token_address" : "0x5a2d2b9b015b46b8eaff7bffdc5db0051db7439b"
-          },
-          {
-              "event": "EventPaymentSentSuccess",
-              "amount": "20",
-              "target": "0x82641569b2062B545431cF6D7F0A418582865ba7",
-              "identifier": "3",
-              "log_time": "2018-10-30T07:10:13.122",
-	      "token_address" : "0x5a2d2b9b015b46b8eaff7bffdc5db0051db7439b"
-          }
+         "0x41BCBC2fD72a731bcc136Cf6F7442e9C19e9f313",
+         "0x5A5f458F6c1a034930E45dC9a64B99d7def06D7E",
+         "0x8942c06FaA74cEBFf7d55B79F9989AdfC85C6b85"
       ]
 
-  :statuscode 200: For successful query
-  :statuscode 404: The given token and / or partner addresses are not valid eip55-encoded Ethereum addresses
-  :statuscode 409: If the given block number or token_address arguments are invalid
-  :statuscode 500: Internal Raiden node error
-  :statuscode 503: The API is currently unavailable, e. g. because the Raiden node is still in the initial sync or shutting down.
-
-
-Querying node state
-===================
+   :statuscode 200: Successfully leaving a token network
+   :statuscode 404: The given token address is not a valid EIP55-encoded Ethereum address
+   :statuscode 500: Internal Raiden node error
+   :statuscode 503: The API is currently unavailable, e. g. because the Raiden node is still in the initial sync or shutting down.
 
-.. http:get:: /api/(version)/status
+   .. note::
+      Currently, the API calls are blocking. This means that in the case of long running calls like leave a token network, if an API call is currently being processed by Raiden, all pending calls will be queued and processed with their passed API call argument.
+      
 
-   Query the node status. Possible answers are:
+Pending Transfers
+=================
 
-   - ``"ready"``: The node is listening on its API endpoints
+The pending transfers endpoints let you query information about transfers that have not been completed yet.
 
-   - ``"syncing"``: The node is still in the initial sync. Number of blocks to sync will also be given.
+.. http:get:: /api/(version)/pending_transfers
 
-   - ``"unavailable"``: The node is unavailable for some other reason
+   Returns a list of all transfers that have not been completed yet.
 
    **Example Request**:
 
    .. http:example:: curl wget httpie python-requests
 
-      GET /api/v1/status HTTP/1.1
+      GET /api/v1/pending_transfers HTTP/1.1
       Host: localhost:5001
 
-   **Example Response**:
-
-   .. sourcecode:: http
-
-      HTTP/1.1 200 OK
-      Content-Type: application/json
-
-      {
-         "status": "syncing",
-         "blocks_to_sync": "130452"
-      }
+   See below for an example response.
 
-   :statuscode 200: Successful query
-   :statuscode 500: Internal Raiden error
+.. http:get:: /api/(version)/pending_transfers/(token_address)
 
+   Limits the response to pending transfers of the specified token.
 
-User Deposit for services
-===================
+   **Example Request**:
 
-For paying the :doc:`Raiden Services <raiden_services>` it is necessary to have RDN (Raiden Network Tokens) in the User Deposit Contract (UDC). 
-This endpoint can be used to deposit to and withdraw from the UDC.
+   .. http:example:: curl wget httpie python-requests
 
+      GET /api/v1/pending_transfers/0xd0A1E359811322d97991E03f863a0C30C2cF029C HTTP/1.1
+      Host: localhost:5001
 
-.. http:post:: /api/(version)/user_deposit
+   See below for an example response.
 
-   **Deposit**
+.. http:get:: /api/(version)/pending_transfers/(token_address)/(partner_address)
 
-   Deposit RDN tokens to the UDC.
+   Limits the response to pending transfers of the specified token and channel.
 
    **Example Request**:
 
    .. http:example:: curl wget httpie python-requests
 
-      POST /api/v1/user_deposit HTTP/1.1
+      GET /api/v1/pending_transfers/0xd0A1E359811322d97991E03f863a0C30C2cF029C/0x2c4b0Bdac486d492E3cD701F4cA87e480AE4C685 HTTP/1.1
       Host: localhost:5001
-      Content-Type: application/json
-
-      {
-          "total_deposit": "200000"
-      }
 
    **Example Response**:
 
@@ -931,33 +1084,46 @@ This endpoint can be used to deposit to and withdraw from the UDC.
       HTTP/1.1 200 OK
       Content-Type: application/json
 
-      {
-         "transaction_hash": "0xc5988c93c07cf1579e73d39ee2a3d6e948d959d11015465a77817e5239165170"
-      }
+      [
+         {
+            "channel_identifier": "255",
+            "initiator": "0x5E1a3601538f94c9e6D2B40F7589030ac5885FE7",
+            "locked_amount": "119",
+            "payment_identifier": "1",
+            "role": "initiator",
+            "target": "0x00AF5cBfc8dC76cd599aF623E60F763228906F3E",
+            "token_address": "0xd0A1E359811322d97991E03f863a0C30C2cF029C",
+            "token_network_address": "0x111157460c0F41EfD9107239B7864c062aA8B978",
+            "transferred_amount": "331"
+         }
+      ]
 
-   :statuscode 200: Deposit was successful
-   :statuscode 400: The provided JSON is in some way malformed
-   :statuscode 402: Insufficient balance to do a deposit or insufficient ETH to pay for the gas of the on-chain transaction
-   :statuscode 404: No UDC is configured on the Raiden node
-   :statuscode 409: The provided ``total_deposit`` is not higher than the previous ``total_deposit`` or attempted to deposit more RDN than the UDC limit would allow
+   :statuscode 200: Successful query
+   :statuscode 404: The queried channel or token was not found
    :statuscode 500: Internal Raiden node error
    :statuscode 503: The API is currently unavailable, e. g. because the Raiden node is still in the initial sync or shutting down.
+   :resjsonarr string role: One of "initiator", "mediator" and "target"
 
-   **Plan a withdraw**
 
-   Before RDN can be withdrawn from the UDC the withdraw must be planned.
+Notifications
+=============
+
+.. http:get:: /api/(version)/notifications
+
+   Raiden can inform the user about conditions that may require an action by the user or their attention.
+   These are the types of notifications that currently can be returned, listed by ``id``:
+
+   * ``low_rdn``: RDN tokens deposited in the UDC are below a threshold.
+   * ``version_outdated``: Running an outdated version of Raiden.
+   * ``missing_gas_reserve``: ETH balance is below a threshold and you may not be able to perform on-chain transactions.
+   * ``version_security_warning``: There is an important security update.
 
    **Example Request**:
 
    .. http:example:: curl wget httpie python-requests
 
-      POST /api/v1/user_deposit HTTP/1.1
+      GET /api/v1/notifications HTTP/1.1
       Host: localhost:5001
-      Content-Type: application/json
-
-      {
-          "planned_withdraw_amount": "1500"
-      }
 
    **Example Response**:
 
@@ -966,34 +1132,35 @@ This endpoint can be used to deposit to and withdraw from the UDC.
       HTTP/1.1 200 OK
       Content-Type: application/json
 
-      {
-         "planned_withdraw_block_number": 4269933,
-         "transaction_hash": "0xec6a0d010d740df20ca8b3b6456e9deaab8abf3787cb676ee244bef7d28aa4fc"
-      }
+      [
+         {
+            "body": "WARNING\nYour account's RDN balance deposited in the UserDepositContract of 0.0 is below the minimum threshold 5.5. Provided that you have either a monitoring service or a path finding service activated, your node is not going to be able to pay those services which may lead to denial of service or loss of funds.",
+            "id": "low_rdn",
+            "summary": "RDN balance too low",
+            "urgency": "normal"
+         },
+         {
+            "body": "You're running version 1.1.1.dev630+g43ae6e32b. The latest version is 1.2.0It's time to update! Releases: https://github.com/raiden-network/raiden/releases",
+            "id": "version_outdated",
+            "summary": "Your version is outdated",
+            "urgency": "normal"
+         }
+      ]
 
-   :statuscode 200: Withdraw plan was successful
-   :statuscode 400: The provided JSON is in some way malformed
-   :statuscode 402: Insufficient ETH to pay for the gas of the on-chain transaction
-   :statuscode 404: No UDC is configured on the Raiden node
-   :statuscode 409: The provided ``planned_withdraw_amount`` is higher than the balance in the UDC or not greater than zero
-   :statuscode 500: Internal Raiden node error
-   :statuscode 503: The API is currently unavailable, e. g. because the Raiden node is still in the initial sync or shutting down.
 
-   **Withdraw**
+Shutdown
+========
+
+.. http:post:: /api/(version)/shutdown
 
-   Withdraw RDN from the UDC. Can only be done after 100 blocks after the withdraw was planned.
+   You can call the shutdown endpoint to stop a running Rainde node.
 
    **Example Request**:
 
    .. http:example:: curl wget httpie python-requests
 
-      POST /api/v1/user_deposit HTTP/1.1
+      POST /api/v1/shutdown HTTP/1.1
       Host: localhost:5001
-      Content-Type: application/json
-
-      {
-          "withdraw_amount": "1500"
-      }
 
    **Example Response**:
 
@@ -1003,24 +1170,21 @@ This endpoint can be used to deposit to and withdraw from the UDC.
       Content-Type: application/json
 
       {
-         "transaction_hash": "0xfc7edd195c6cc0c9391d84dd83b7aa9dbfffbcfc107e5c33a5ab912c0d92416c"
+         "status": "shutdown"
       }
 
-   :statuscode 200: Withdraw was successful
-   :statuscode 400: The provided JSON is in some way malformed
-   :statuscode 402: Insufficient ETH to pay for the gas of the on-chain transaction
-   :statuscode 404: No UDC is configured on the Raiden node
-   :statuscode 409: The provided ``withdraw_amount`` is higher than the planned amount or not greater than zero or the withdraw is too early
-   :statuscode 500: Internal Raiden node error
-   :statuscode 503: The API is currently unavailable, e. g. because the Raiden node is still in the initial sync or shutting down.
 
+Testing
+=======
+
+You can mint tokens for testing purposes by making a request to the ``_testing`` endpoint. 
+This is only possible on testnets.
 
-API endpoints for testing
-=========================
+**Mint Tokens**
 
 .. http:post:: /api/(version)/_testing/tokens/(token_address)/mint
 
-   Mint tokens. This requires the token at ``token_address`` to implement a minting method with one of
+   This requires the token at ``token_address`` to implement a minting method with one of
    the common interfaces:
 
    - ``mint(address,uint256)``
@@ -1029,7 +1193,7 @@ API endpoints for testing
 
    - ``increaseSupply(uint256,address)``
 
-   Depending on the token, it may also be necessary to have minter privilege.
+   Depending on the token, it may also be necessary to have minter privileges.
 
    **Example Request**:
 
@@ -1045,7 +1209,7 @@ API endpoints for testing
       }
 
    :reqjson address to: The address to assign the minted tokens to.
-   :reqjson int value: The amount of tokens to be minted.
+   :reqjson string value: The amount of tokens to be minted.
 
    **Example Response**:
 
diff --git a/docs/the-raiden-web-interface/add-more-tokens.inc.rst b/docs/the-raiden-web-interface/add-more-tokens.inc.rst
index 96dab89290..2a98b6cd0b 100644
--- a/docs/the-raiden-web-interface/add-more-tokens.inc.rst
+++ b/docs/the-raiden-web-interface/add-more-tokens.inc.rst
@@ -1,6 +1,9 @@
 Add More Tokens
 ===============
 
+At some point, you might need to top up your channels with tokens to be able to send more payments. 
+You can allocate more tokens by depositing to a channel.
+
 Deposit to a channel
 --------------------
 
diff --git a/docs/the-raiden-web-interface/close-channels-and-settle-payments.inc.rst b/docs/the-raiden-web-interface/close-channels-and-settle-payments.inc.rst
index c848d4ef5c..4171080d48 100644
--- a/docs/the-raiden-web-interface/close-channels-and-settle-payments.inc.rst
+++ b/docs/the-raiden-web-interface/close-channels-and-settle-payments.inc.rst
@@ -1,11 +1,14 @@
 Settle Payments and Close Channels
 ==================================
 
+To get your tokens back on-chain you need to either withdraw them from a channel or close and settle the channel.
 You have a couple of ways for getting your tokens back on-chain:
 
--  Withdraw tokens from an open
--  Close a channel
--  Leave a token network
+-  :ref:`Withdraw tokens from an open channel <webui_withdraw_tokens>`
+-  :ref:`Close a channel <webui_close_channel>`
+-  :ref:`Leave a token network <webui_leave_network>`
+
+.. _webui_withdraw_tokens:
 
 Withdraw tokens
 ---------------
@@ -45,6 +48,7 @@ participants.
    channel is settled once the settle timeout period is over. The tokens can
    then be used on-chain. 
 
+.. _webui_leave_network:
 
 Leave a token network
 ---------------------
diff --git a/docs/the-raiden-web-interface/join-a-token-network.inc.rst b/docs/the-raiden-web-interface/join-a-token-network.inc.rst
index 7f9d69b09e..8eadf074f2 100644
--- a/docs/the-raiden-web-interface/join-a-token-network.inc.rst
+++ b/docs/the-raiden-web-interface/join-a-token-network.inc.rst
@@ -3,6 +3,7 @@
 Join a Token Network
 ====================
 
+To make payments in Raiden you first need to join the network by opening channels for the token you want to make payments with.
 You can join a token network by either:
 
 -  :ref:`Quick Connect <webui_quick_connect>`
@@ -16,7 +17,7 @@ You can join a token network by either:
    supply of tokens.
 
    These token contracts are like a registry where different amounts of the
-   total supply is mapped to different owners. {% endhint %}
+   total supply is mapped to different owners.
 
 .. note::
 
diff --git a/docs/the-raiden-web-interface/payment.inc.rst b/docs/the-raiden-web-interface/payment.inc.rst
index 2c522c6b63..b932063ec3 100644
--- a/docs/the-raiden-web-interface/payment.inc.rst
+++ b/docs/the-raiden-web-interface/payment.inc.rst
@@ -3,6 +3,7 @@
 Make a Payment
 ==============
 
+Payment channels allow for Raiden transfers to be made without the need to involve the actual blockchain.
 If you have one or more channels open you can start making payments. If
 you used :ref:`Quick Connect <webui_quick_connect>` three
 channels were automatically opened with three nodes.
diff --git a/docs/the-raiden-web-interface/screens.inc.rst b/docs/the-raiden-web-interface/screens.inc.rst
index a1c9541929..83668a3fe0 100644
--- a/docs/the-raiden-web-interface/screens.inc.rst
+++ b/docs/the-raiden-web-interface/screens.inc.rst
@@ -3,6 +3,7 @@
 Navigate the WebUI
 ==================
 
+To get you familiarized with the Raiden WebUI we have put together a short introduction to the most important screens of the application.
 You will find the navigation menu on the right and on the top is a bar
 which contains general information.
 
@@ -12,11 +13,17 @@ The **top bar** displays:
    activity happens. If you are using any testnet you'll also find a
    button which redirects you to the respectiva faucet for acquiring
    ETH.
-2. The **address** of your Raiden node (you can display the address as a
+2. Your **UDC balance**. This is the amount of RDN tokens you have deposited
+   in the User Deposit Contract. A deposit is needed for paying the 
+   :doc:`Raiden Services <../raiden_services>`. When clicking the button next
+   to the balance you will see a :ref:`dialog <webui_udc>` for performing 
+   deposits and withdrawals.
+3. Your **on-chain token balance** of the currently selected token.
+4. The **address** of your Raiden node (you can display the address as a
    QR code).
-3. A **search** field which filters all items on the page based on
+5. A **search** field which filters all items on the page based on
    search term.
-4. The **notification** button.
+6. The **notification** button.
 
 Home
 ----
@@ -26,9 +33,11 @@ the same things on Home screen as on each subpage we won't cover the
 Home screen in detail here but rather focus on the navigation menu
 options.
 
--  Transfers
--  Contacts
--  Channels
+-  :ref:`Transfers <webui_transfers>`
+-  :ref:`Contacts <webui_contacts>`
+-  :ref:`Channels <webui_channels>`
+
+.. _webui_transfers:
 
 Transfers
 ---------
@@ -51,6 +60,8 @@ The history is filtered by the selected token network.
 
 .. note:: If you have selected a network when using a testnet you can use the **"Add new network"** button for registering new token networks.
 
+.. _webui_contacts:
+
 Contacts
 --------
 
@@ -66,6 +77,8 @@ or delete the chosen contact.
 
 .. note:: Contacts can be imported and exported by using the button on the top right.
 
+.. _webui_channels:
+
 Channels
 --------
 
@@ -84,7 +97,7 @@ balance. By clicking the three dots you can:
 
 .. note::
 
-    **What is a channel?**
+   **What is a channel?**
 
    A payment channel enables tokens to be exchanged back and forth between
    parties without involving the actual blockchain. A payment channel in
diff --git a/docs/the-raiden-web-interface/the-raiden-web-interface.rst b/docs/the-raiden-web-interface/the-raiden-web-interface.rst
index d0b241bfed..1e43755aab 100644
--- a/docs/the-raiden-web-interface/the-raiden-web-interface.rst
+++ b/docs/the-raiden-web-interface/the-raiden-web-interface.rst
@@ -1,5 +1,9 @@
-Overview
-########
+Raiden Web Interface Tutorial
+#############################
+
+The Raiden web interface (WebUI) helps you to interact with your Raiden node and aims to inspire to create own applications that utilize the Raiden REST API endpoints.
+
+.. image:: webui.gif
 
 Run the Raiden web application
 ==============================
@@ -12,6 +16,8 @@ on** `http://localhost:5001 <http://localhost:5001/>`__\ **.**
 
 .. include:: screens.inc.rst
 
+.. include:: user-deposit.inc.rst
+
 .. include:: join-a-token-network.inc.rst
 
 .. include:: payment.inc.rst
diff --git a/docs/the-raiden-web-interface/user-deposit.inc.rst b/docs/the-raiden-web-interface/user-deposit.inc.rst
new file mode 100644
index 0000000000..b7423b68af
--- /dev/null
+++ b/docs/the-raiden-web-interface/user-deposit.inc.rst
@@ -0,0 +1,24 @@
+.. _webui_udc:
+
+Manage UDC funds
+================
+
+On the top bar you can click the button next to the **"UDC Deposit"** to open a dialog for managing your funds in the User Deposit Contract.
+This dialog allows you to perform deposits and withdrawals. 
+It will show you the amount of Raiden Network Tokens (RDN) you have on-chain, which are necessary for deposits.
+
+For a deposit, click the **"Deposit"** button and enter the amount of RDN tokens you would like to have in the UDC.
+This will trigger all necessary on-chain transactions.
+
+For a withdrawal, you first need to click **"Plan Withdrawal"**.
+After entering the amount of tokens you would like to withdraw, you have to wait for at least 100 blocks on-chain before you can perform the actual withdrawal.
+When these 100 blocks have passed, a **"Withdraw"** button will show up.
+Clicking it will trigger another on-chain transaction.
+
+.. note::
+
+   **What is the User Deposit Contract (UDC)?**
+
+   The UDC is a smart contract where you deposit RDN (Raiden Network Tokens) for later paying the :doc:`Raiden Services <../raiden_services>`.
+   For sending a :ref:`mediated transfer <mainnet_tutorial_mediated_payments>` it is even necessary to have RDN tokens in the UDC.
+
diff --git a/docs/the-raiden-web-interface/webui.gif b/docs/the-raiden-web-interface/webui.gif
new file mode 100644
index 0000000000..bc3db96058
Binary files /dev/null and b/docs/the-raiden-web-interface/webui.gif differ
diff --git a/docs/using-raiden-on-mainnet/deposit-tokens-to-the-udc.inc.rst b/docs/using-raiden-on-mainnet/deposit-tokens-to-the-udc.inc.rst
index 48716f2e7d..af63cfa134 100644
--- a/docs/using-raiden-on-mainnet/deposit-tokens-to-the-udc.inc.rst
+++ b/docs/using-raiden-on-mainnet/deposit-tokens-to-the-udc.inc.rst
@@ -1,38 +1,21 @@
+.. _mainnet_tutorial_deposit_udc:
+
 Deposit Tokens to the UDC
 =========================
 
-This section will describe how to manually add Raiden Network Tokens to
-the UDC by:
-
-1. Approving the UDC to use RDN
-2. Depositing RDN to the UDC
-
-Approving the UDC to use RDN
-----------------------------
-
-We need to approve a value of Raiden Network Tokens that the UDC is
-allowed to spend. To do so:
+To do :ref:`mediated transfers <mainnet_tutorial_mediated_payments>` we need to have RDN (Raiden Network Tokens) in the UDC (User Deposit Contract) for paying the monitoring and pathfinding services.
+This section will describe how to add Raiden Network Tokens to the UDC by making a call to the :ref:`User Deposit endpoint <api_user_deposit>` of the Raiden API.
+The following POST request will deposit 100 RDN tokens to the UDC:
 
-1. Visit the `RDN token on
-   Etherscan <https://etherscan.io/address/0x255Aa6DF07540Cb5d3d297f0D0D4D84cb52bc8e6#writeContract>`__.
-2. Go to the "**approve"** field under "**Write Contract"**.
-3. Enter ``0x1c62fF66aF8aaD410065E02338F5bFbbe23e1f10`` (the mainnet UDC
-   address) as argument for **"_spender"**.
-4. Enter an amount of ``100000000000000000000`` (corresponding to 100
-   RDN) as argument for **"_value"**.
-5. Click on **"Write"** to approve.
+.. code:: bash
 
-Depositing RDN to the UDC
--------------------------
+   curl -i -X POST \
+   http://localhost:5001/api/v1/user_deposit \
+   -H 'Content-Type: application/json' \
+   --data-raw '{"total_deposit": "100000000000000000000"}'
 
-Once we have approved an RDN amount all that reamins is for us to
-deposit some tokens to the UDC.
+.. note::
+   Raiden utilizes a RESTful API where all URL paths starts with ``/api/`` followed by a version number. The current API version is ``1`` and therefore all requests begins with ``/api/v1/``.
 
-1. Visit the `UDC on
-   Etherscan <https://etherscan.io/address/0x1c62fF66aF8aaD410065E02338F5bFbbe23e1f10#writeContract>`__.
-2. Go to the **"deposit"** field under **"Write Contract"**.
-3. Enter the address of the Raiden node you want to use as argument for
-   **"beneficiary"**.
-4. Enter an amount of e.g. ``100000000000000000000`` (corresponding to
-   100 RDN) as argument for **"new_total_deposit"**.
-5. Click on **"Write"** to make the deposit.
+The request will take a couple of minutes, because two on-chain transactions are performed: Approving the UDC to use RDN and Depositing RDN to the UDC.
+When successfully completed the API will respond with a transaction hash.
diff --git a/docs/using-raiden-on-mainnet/join-a-token-network-1.inc.rst b/docs/using-raiden-on-mainnet/join-a-token-network-1.inc.rst
index 041b166c65..6eac9486e9 100644
--- a/docs/using-raiden-on-mainnet/join-a-token-network-1.inc.rst
+++ b/docs/using-raiden-on-mainnet/join-a-token-network-1.inc.rst
@@ -1,6 +1,8 @@
 Open a Channel
 ==============
 
+Opening a channel and depositing tokens in it allows you to transfer these tokens 
+to your channel partner - and indirectly to other nodes connected to you partner.
 To open a channel, a PUT request is made to the
 :ref:`channels endpoint <api_open_channel>` that includes a JSON object containing:
 
@@ -21,10 +23,6 @@ To open a channel, a PUT request is made to the
    -H 'Content-Type: application/json' \
    --data-raw '{"partner_address": "0x61C808D82A3Ac53231750daDc13c777b59310bD9", "token_address": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", "total_deposit": "1337", "settle_timeout": "500"}'
 
-.. note::
-
-    Raiden utilizes a RESTful API where all URL paths starts with ``/api/`` followed by a version number. The current API version is ``1`` and therefore all requests begins with ``/api/v1/``.
-
 
 This will create a new channel and a successful request will return you
 the following response object:
diff --git a/docs/using-raiden-on-mainnet/make-a-payment.inc.rst b/docs/using-raiden-on-mainnet/make-a-payment.inc.rst
index 5f61e58624..65bd8a8a97 100644
--- a/docs/using-raiden-on-mainnet/make-a-payment.inc.rst
+++ b/docs/using-raiden-on-mainnet/make-a-payment.inc.rst
@@ -36,6 +36,8 @@ You might want to open a direct channel if you know you'll be making
 frequent payments to a specific peer. Each payment will then go straight
 to that peer via the open channel. This is called a direct payment.
 
+.. _mainnet_tutorial_mediated_payments:
+
 Mediated payments
 -----------------
 
diff --git a/pyproject.toml b/pyproject.toml
index 1cd8677f47..68926857e3 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
 [tool.black]
 line-length = 99
-target-version = ['py37']
+target-version = ['py37', 'py38', 'py39']
 include = '\.pyi?$'
 exclude = '''
 /(
@@ -12,3 +12,23 @@ exclude = '''
   | dist
 )/
 '''
+
+[tool.pytest.ini_options]
+testpaths = [
+    "raiden/tests",
+]
+timeout_limit_for_setup_and_call = 240
+timeout_limit_teardown = 15
+norecursedirs = [
+    "node_modules",
+]
+# Ignore warnings:
+# - urllib3 unverified TLS connection
+filterwarnings = [
+    "ignore::urllib3.exceptions.InsecureRequestWarning",
+]
+markers = [
+    "timeout",
+    "asyncio: tests that require an asyncio eventloop",
+]
+junit_family = "xunit1"
diff --git a/raiden.spec b/raiden.spec
index be360081f4..9f7860cc25 100644
--- a/raiden.spec
+++ b/raiden.spec
@@ -12,18 +12,28 @@ PyInstaller spec file to build single file or dir distributions
 """
 
 # Set to false to produce an exploded single-dir
-ONEFILE = int(os.environ.get('ONEFILE', True))
-
-
-def Entrypoint(dist, group, name, scripts=None, pathex=None, hiddenimports=None, hookspath=None,
-               excludes=None, runtime_hooks=None, datas=None):
+ONEFILE = int(os.environ.get("ONEFILE", True))
+
+
+def Entrypoint(
+    dist,
+    group,
+    name,
+    scripts=None,
+    pathex=None,
+    hiddenimports=None,
+    hookspath=None,
+    excludes=None,
+    runtime_hooks=None,
+    datas=None,
+):
     import pkg_resources
 
     # get toplevel packages of distribution from metadata
     def get_toplevel(dist):
         distribution = pkg_resources.get_distribution(dist)
-        if distribution.has_metadata('top_level.txt'):
-            return list(distribution.get_metadata('top_level.txt').split())
+        if distribution.has_metadata("top_level.txt"):
+            return list(distribution.get_metadata("top_level.txt").split())
         else:
             return []
 
@@ -42,54 +52,70 @@ def Entrypoint(dist, group, name, scripts=None, pathex=None, hiddenimports=None,
     # insert path of the egg at the verify front of the search path
     pathex = [ep.dist.location] + pathex
     # script name must not be a valid module name to avoid name clashes on import
-    script_path = os.path.join(workpath, name + '-script.py')
+    script_path = os.path.join(workpath, name + "-script.py")
     print("creating script for entry point", dist, group, name)
-    with open(script_path, 'w') as fh:
+    with open(script_path, "w") as fh:
         print("import", ep.module_name, file=fh)
-        print("%s.%s()" % (ep.module_name, '.'.join(ep.attrs)), file=fh)
+        print("%s.%s()" % (ep.module_name, ".".join(ep.attrs)), file=fh)
         for package in packages:
             print("import", package, file=fh)
 
-    return Analysis(
+    analysis = Analysis(
         [script_path] + scripts,
         pathex=pathex,
         hiddenimports=hiddenimports,
         hookspath=hookspath,
         excludes=excludes,
         runtime_hooks=runtime_hooks,
-        datas=datas
+        datas=datas,
     )
+    return analysis
 
 
-if hasattr(pdb, 'pdb'):
+if hasattr(pdb, "pdb"):
     # pdbpp moves the stdlib pdb to the `pdb` attribute of it's own patched pdb module
     raise RuntimeError(
-        'pdbpp is installed which causes broken PyInstaller builds. Please uninstall it.',
+        "pdbpp is installed which causes broken PyInstaller builds. Please uninstall it.",
     )
 
 
 # We don't need Tk and friends
-sys.modules['FixTk'] = None
+sys.modules["FixTk"] = None
 
-executable_name = 'raiden-{}-{}-{}'.format(
-    os.environ.get('ARCHIVE_TAG', 'v' + get_system_spec()['raiden']),
-    'macOS' if platform.system() == 'Darwin' else platform.system().lower(),
-    platform.machine()
+executable_name = "raiden-{}-{}-{}".format(
+    os.environ.get("ARCHIVE_TAG", "v" + get_system_spec()["raiden"]),
+    "macOS" if platform.system() == "Darwin" else platform.system().lower(),
+    platform.machine(),
 )
 
 a = Entrypoint(
-    'raiden',
-    'console_scripts',
-    'raiden',
-    hookspath=['tools/pyinstaller_hooks'],
+    "raiden",
+    "console_scripts",
+    "raiden",
+    hookspath=["tools/pyinstaller_hooks"],
     runtime_hooks=[
-        'tools/pyinstaller_hooks/runtime_gevent_monkey.py',
-        'tools/pyinstaller_hooks/runtime_encoding.py',
-        'tools/pyinstaller_hooks/runtime_raiden_contracts.py',
+        "tools/pyinstaller_hooks/runtime_gevent_monkey.py",
+        "tools/pyinstaller_hooks/runtime_aiortc_pyav_stub.py",
+        "tools/pyinstaller_hooks/runtime_encoding.py",
+        "tools/pyinstaller_hooks/runtime_raiden_contracts.py",
+    ],
+    hiddenimports=[
+        "aiortc_pyav_stub",
     ],
-    hiddenimports=['scrypt', 'eth_tester'],
     datas=[],
-    excludes=['FixTk', 'tcl', 'tk', '_tkinter', 'tkinter', 'Tkinter'],
+    excludes=[
+        "_tkinter",
+        "av",
+        "FixTk",
+        "ipython",
+        "jupyter",
+        "jupyter_core",
+        "notebook",
+        "tcl",
+        "tk",
+        "tkinter",
+        "Tkinter",
+    ],
 )
 
 pyz = PYZ(a.pure, a.zipped_data, cipher=None)
@@ -103,10 +129,10 @@ if ONEFILE:
         a.datas,
         name=executable_name,
         debug=False,
-        strip=True,
+        strip=False,
         upx=False,
         runtime_tmpdir=None,
-        console=True
+        console=True,
     )
 else:
     exe = EXE(
@@ -114,10 +140,10 @@ else:
         a.scripts,
         exclude_binaries=True,
         name=executable_name,
-        debug=True,
+        debug="all",
         strip=False,
         upx=False,
-        console=True
+        console=True,
     )
     coll = COLLECT(
         exe,
@@ -126,5 +152,5 @@ else:
         a.datas,
         strip=False,
         upx=False,
-        name='raiden'
+        name="raiden",
     )
diff --git a/raiden/accounts.py b/raiden/accounts.py
index 0bdbc907c5..00e8eee64f 100644
--- a/raiden/accounts.py
+++ b/raiden/accounts.py
@@ -16,17 +16,17 @@
 
 
 class InvalidAccountFile(Exception):
-    """ Thrown when a file is not a valid keystore account file """
+    """Thrown when a file is not a valid keystore account file"""
 
     pass
 
 
 class KeystoreFileNotFound(RaidenError):
-    """ A keystore file for a user provided account could not be found. """
+    """A keystore file for a user provided account could not be found."""
 
 
 class KeystoreAuthenticationError(RaidenError):
-    """ The provided password could not authenticated the ethereum keystore. """
+    """The provided password could not authenticated the ethereum keystore."""
 
 
 def _find_datadir() -> Optional[str]:  # pragma: no cover
@@ -131,7 +131,7 @@ def get_privkey(self, address: AddressHex, password: str) -> PrivateKey:
 
 
 class Account:
-    """Represents an account.  """
+    """Represents an account."""
 
     def __init__(self, keystore: Dict, password: str = None, path: str = None):
         """
diff --git a/raiden/api/python.py b/raiden/api/python.py
index 8d94790275..73fb7716cf 100644
--- a/raiden/api/python.py
+++ b/raiden/api/python.py
@@ -4,9 +4,10 @@
 
 from raiden import waiting
 from raiden.api.exceptions import ChannelNotFound, NonexistingChannel
-from raiden.constants import NULL_ADDRESS_BYTES, UINT64_MAX, UINT256_MAX
+from raiden.constants import BLOCK_ID_PENDING, NULL_ADDRESS_BYTES, UINT64_MAX, UINT256_MAX
 from raiden.exceptions import (
     AlreadyRegisteredTokenAddress,
+    ConfigurationError,
     DepositMismatch,
     DepositOverLimit,
     DuplicatedChannelError,
@@ -29,6 +30,7 @@
     UserDepositNotConfigured,
     WithdrawMismatch,
 )
+from raiden.network.pathfinding import query_address_metadata
 from raiden.settings import DEFAULT_RETRY_TIMEOUT, PythonApiConfig
 from raiden.storage.utils import TimestampedEvent
 from raiden.transfer import channel, views
@@ -319,7 +321,7 @@ def token_network_leave(
         token_address: TokenAddress,
         retry_timeout: NetworkTimeout = DEFAULT_RETRY_TIMEOUT,
     ) -> List[NettingChannelState]:
-        """ Close all channels and wait for settlement. """
+        """Close all channels and wait for settlement."""
         if not is_binary_address(registry_address):
             raise InvalidBinaryAddress("registry_address must be a valid address in binary")
         if not is_binary_address(token_address):
@@ -350,14 +352,14 @@ def is_already_existing_channel(
         self,
         token_network_address: TokenNetworkAddress,
         partner_address: Address,
-        block_identifier: Optional[BlockIdentifier] = None,
+        block_identifier: BlockIdentifier = BLOCK_ID_PENDING,
     ) -> bool:
         proxy_manager = self.raiden.proxy_manager
         proxy = proxy_manager.address_to_token_network[token_network_address]
         channel_identifier = proxy.get_channel_identifier_or_none(
             participant1=self.raiden.address,
             participant2=partner_address,
-            block_identifier=block_identifier or proxy_manager.client.get_checking_block(),
+            block_identifier=block_identifier,
         )
 
         return channel_identifier is not None
@@ -587,8 +589,18 @@ def set_total_channel_withdraw(
                 )
             )
 
+        pfs_config = self.raiden.config.pfs_config
+        recipient_address = channel_state.partner_state.address
+        if pfs_config is None:
+            raise ConfigurationError("Can't query metadata without PFS config")
+
+        recipient_metadata = query_address_metadata(
+            pfs_config=pfs_config, user_address=recipient_address
+        )
         self.raiden.withdraw(
-            canonical_identifier=channel_state.canonical_identifier, total_withdraw=total_withdraw
+            canonical_identifier=channel_state.canonical_identifier,
+            total_withdraw=total_withdraw,
+            recipient_metadata=recipient_metadata,
         )
 
         waiting.wait_for_withdraw_complete(
@@ -947,15 +959,11 @@ def get_channel_list(
         return result
 
     def get_node_network_state(self, node_address: Address) -> NetworkState:
-        """ Returns the currently network status of `node_address`. """
+        """Returns the currently network status of `node_address`."""
         return views.get_node_network_status(
             chain_state=views.state_from_raiden(self.raiden), node_address=node_address
         )
 
-    def async_start_health_check_for(self, node_address: Address) -> None:
-        """ Returns the currently network status of `node_address`. """
-        self.raiden.async_start_health_check_for(node_address)
-
     def get_tokens_list(self, registry_address: TokenNetworkRegistryAddress) -> List[TokenAddress]:
         """Returns a list of tokens the node knows about"""
         return views.get_token_identifiers(
@@ -984,7 +992,7 @@ def transfer_and_wait(
         secrethash: SecretHash = None,
         lock_timeout: BlockTimeout = None,
     ) -> "PaymentStatus":
-        """ Do a transfer with `target` with the given `amount` of `token_address`. """
+        """Do a transfer with `target` with the given `amount` of `token_address`."""
         # pylint: disable=too-many-arguments
 
         payment_status = self.transfer_async(
diff --git a/raiden/api/rest.py b/raiden/api/rest.py
index 1e3cb79f28..97642dc438 100644
--- a/raiden/api/rest.py
+++ b/raiden/api/rest.py
@@ -3,6 +3,7 @@
 import socket
 from hashlib import sha256
 from http import HTTPStatus
+from typing import Type
 
 import gevent
 import gevent.pool
@@ -88,6 +89,7 @@
     PaymentConflict,
     RaidenRecoverableError,
     SamePeerAddress,
+    ServiceRequestFailed,
     TokenNetworkDeprecated,
     TokenNotRegistered,
     UnexpectedChannelState,
@@ -187,14 +189,14 @@ def endpoint_not_found(e: Any) -> Response:
 
 
 def hexbytes_to_str(map_: Dict) -> None:
-    """ Converts values that are of type `HexBytes` to strings. """
+    """Converts values that are of type `HexBytes` to strings."""
     for k, v in map_.items():
         if isinstance(v, HexBytes):
             map_[k] = encode_hex(v)
 
 
 def encode_byte_values(map_: Dict) -> None:
-    """ Converts values that are of type `bytes` to strings. """
+    """Converts values that are of type `bytes` to strings."""
     for k, v in map_.items():
         if isinstance(v, bytes):
             map_[k] = encode_hex(v)
@@ -226,10 +228,9 @@ def restapi_setup_urls(flask_api_context: Api, rest_api: "RestAPI", urls: List)
 
 
 def restapi_setup_type_converters(
-    flask_app: Flask, names_to_converters: Dict[str, BaseConverter]
+    flask_app: Flask, names_to_converters: Dict[str, Type[BaseConverter]]
 ) -> None:
-    for key, value in names_to_converters.items():
-        flask_app.url_map.converters[key] = value
+    flask_app.url_map.converters.update(names_to_converters)
 
 
 class APIServer(Runnable):  # pragma: no unittest
@@ -273,7 +274,7 @@ def __init__(
         flask_api_context = Api(blueprint, prefix=self._api_prefix)
 
         restapi_setup_type_converters(
-            flask_app, {"hexaddress": cast(BaseConverter, HexAddressConverter)}
+            flask_app, {"hexaddress": cast(Type[BaseConverter], HexAddressConverter)}
         )
 
         restapi_setup_urls(flask_api_context, rest_api, URLS_V1)
@@ -292,7 +293,11 @@ def __init__(
 
         self.flask_app.register_error_handler(HTTPStatus.NOT_FOUND, endpoint_not_found)
         self.flask_app.register_error_handler(Exception, self.unhandled_exception)
-        self.flask_app.before_request(self._check_shutdown_before_handle_request)
+
+        # FIXME: There's a type annotation bug on flask, fixed on this commit:
+        # https://github.com/pallets/flask/commit/a960236117442bec67f89c30dfa014e05483da5a
+        # Remove type ignore on the next flask release this commit is included
+        self.flask_app.before_request(self._check_shutdown_before_handle_request)  # type: ignore
 
         # needed so flask_restful propagates the exception to our error handler above
         # or else, it'll replace it with a E500 response
@@ -305,8 +310,10 @@ def __init__(
                 )
 
     def _check_shutdown_before_handle_request(self) -> Optional[Response]:
-        # We don't want to handle requests when shutting down
-        # When the `before_request` hook returns a value, the request will not be processed further
+        """
+        We don't want to handle requests when shutting down
+        When the `before_request` hook returns a value, the request will not be processed further
+        """
         if self.stop_event.is_set():
             return api_error("Raiden API is shutting down", HTTPStatus.SERVICE_UNAVAILABLE)
 
@@ -429,7 +436,7 @@ def stop(self) -> None:
         )
 
     def unhandled_exception(self, exception: Exception) -> Response:
-        """ Flask.errorhandler when an exception wasn't correctly handled """
+        """Flask.errorhandler when an exception wasn't correctly handled"""
         log.critical(
             "Unhandled exception when processing endpoint request",
             exc_info=True,
@@ -1129,7 +1136,7 @@ def _withdraw(
             )
         except (NonexistingChannel, UnknownTokenAddress) as e:
             return api_error(errors=str(e), status_code=HTTPStatus.BAD_REQUEST)
-        except (InsufficientFunds, WithdrawMismatch) as e:
+        except (InsufficientFunds, WithdrawMismatch, ServiceRequestFailed) as e:
             return api_error(errors=str(e), status_code=HTTPStatus.CONFLICT)
         # TODO handle InsufficientEth here
 
diff --git a/raiden/api/rest_utils.py b/raiden/api/rest_utils.py
index 033e42612d..4ca610be9d 100644
--- a/raiden/api/rest_utils.py
+++ b/raiden/api/rest_utils.py
@@ -49,7 +49,7 @@ def api_error(errors: Any, status_code: HTTPStatus) -> Response:
 
 
 def if_api_available(method: Callable) -> Callable:
-    """ Decorator for resource methods which only work if the API is fully available. """
+    """Decorator for resource methods which only work if the API is fully available."""
 
     def decorated(self, *args, **kwargs):  # type: ignore
         if not self.rest_api.available:
diff --git a/raiden/blockchain/events.py b/raiden/blockchain/events.py
index 2ea1bb53c0..35592a69da 100644
--- a/raiden/blockchain/events.py
+++ b/raiden/blockchain/events.py
@@ -130,7 +130,7 @@ def get_all_netting_channel_events(
     from_block: BlockIdentifier = GENESIS_BLOCK_NUMBER,
     to_block: BlockIdentifier = BLOCK_ID_LATEST,
 ) -> List[Dict]:  # pragma: no unittest
-    """ Helper to get all events of a NettingChannelContract. """
+    """Helper to get all events of a NettingChannelContract."""
 
     filter_args = get_filter_args_for_all_events_from_channel(
         token_network_address=token_network_address,
diff --git a/raiden/blockchain/filters.py b/raiden/blockchain/filters.py
index ac41f77139..e2d6b6030e 100644
--- a/raiden/blockchain/filters.py
+++ b/raiden/blockchain/filters.py
@@ -47,7 +47,7 @@ def get_filter_args_for_specific_event_from_channel(
     from_block: BlockIdentifier = GENESIS_BLOCK_NUMBER,
     to_block: BlockIdentifier = BLOCK_ID_LATEST,
 ) -> FilterParams:
-    """ Return the filter params for a specific event of a given channel. """
+    """Return the filter params for a specific event of a given channel."""
     event_abi = contract_manager.get_event_abi(CONTRACT_TOKEN_NETWORK, event_name)
 
     # Here the topics for a specific event are created
@@ -72,7 +72,7 @@ def get_filter_args_for_all_events_from_channel(
     from_block: BlockIdentifier = GENESIS_BLOCK_NUMBER,
     to_block: BlockIdentifier = BLOCK_ID_LATEST,
 ) -> FilterParams:
-    """ Return the filter params for all events of a given channel. """
+    """Return the filter params for all events of a given channel."""
 
     event_filter_params = get_filter_args_for_specific_event_from_channel(
         token_network_address=token_network_address,
@@ -145,7 +145,7 @@ def to_web3_filters(
         to_block: BlockNumber,
         node_address: Address,
     ) -> List[FilterParams]:
-        """ Return a filter dict than can be used with web3's ``getLogs`` """
+        """Return a filter dict than can be used with web3's ``getLogs``"""
         tn_event_topics = get_topics_of_events(
             contract_manager.get_contract_abi(CONTRACT_TOKEN_NETWORK)
         )
@@ -250,7 +250,7 @@ def to_web3_filters(
         return cast(List[FilterParams], filters)
 
     def abi_of_contract_address(self, contract_manager: ContractManager) -> Dict[Address, ABI]:
-        """ This class knows which ABI is behind each filtered contract address """
+        """This class knows which ABI is behind each filtered contract address"""
         tnr_abi = contract_manager.get_contract_abi(CONTRACT_TOKEN_NETWORK_REGISTRY)
         tn_abi = contract_manager.get_contract_abi(CONTRACT_TOKEN_NETWORK)
         secret_registry_abi = contract_manager.get_contract_abi(CONTRACT_SECRET_REGISTRY)
@@ -266,7 +266,7 @@ def abi_of_contract_address(self, contract_manager: ContractManager) -> Dict[Add
         return abis
 
     def union(self, other: "RaidenContractFilter") -> "RaidenContractFilter":
-        """ Return a new RaidenContractFilter with all elements from both input filters """
+        """Return a new RaidenContractFilter with all elements from both input filters"""
         # We must not have two different non-None secret registries. Choose the non-None one.
         non_none_secret_registries = {
             self.secret_registry_address,
diff --git a/raiden/blockchain_events_handler.py b/raiden/blockchain_events_handler.py
deleted file mode 100644
index acd05e92f8..0000000000
--- a/raiden/blockchain_events_handler.py
+++ /dev/null
@@ -1,23 +0,0 @@
-from typing import TYPE_CHECKING
-
-from raiden.transfer.architecture import StateChange
-from raiden.transfer.state_change import ContractReceiveChannelNew
-from raiden.utils.typing import MYPY_ANNOTATION
-
-if TYPE_CHECKING:
-    # pylint: disable=unused-import
-    from raiden.raiden_service import RaidenService  # noqa: F401
-
-
-def after_new_channel_start_healthcheck(
-    raiden: "RaidenService", channelnew: ContractReceiveChannelNew
-) -> None:
-    """Start connection healthcheck once a new channel is opened."""
-    partner_address = channelnew.channel_state.partner_state.address
-    raiden.async_start_health_check_for(partner_address)
-
-
-def after_blockchain_statechange(raiden: "RaidenService", state_change: StateChange) -> None:
-    if type(state_change) == ContractReceiveChannelNew:
-        assert isinstance(state_change, ContractReceiveChannelNew), MYPY_ANNOTATION
-        after_new_channel_start_healthcheck(raiden, state_change)
diff --git a/raiden/constants.py b/raiden/constants.py
index c0d65da0a4..5d77cde860 100644
--- a/raiden/constants.py
+++ b/raiden/constants.py
@@ -26,10 +26,10 @@
 
 LATEST = "https://api.github.com/repos/raiden-network/raiden/releases/latest"
 RELEASE_PAGE = "https://github.com/raiden-network/raiden/releases"
-DOC_URL = "https://docs.raiden.network/raiden-api-1"
+DOC_URL = "https://raiden-network.readthedocs.io/en/latest/rest_api.html"
 SECURITY_EXPRESSION = r"\[CRITICAL UPDATE.*?\]"
 
-RAIDEN_DB_VERSION = RaidenDBVersion(26)
+RAIDEN_DB_VERSION = RaidenDBVersion(27)
 SQLITE_MIN_REQUIRED_VERSION = (3, 9, 0)
 PROTOCOL_VERSION = RaidenProtocolVersion(1)
 
@@ -213,11 +213,11 @@ class RoutingMode(Enum):
 ETH_GET_LOGS_THRESHOLD_SLOW = ETH_GET_LOGS_TIMEOUT // 2
 
 # Keep in sync with .circleci/config.yaml
-HIGHEST_SUPPORTED_GETH_VERSION = "1.9.21"
+HIGHEST_SUPPORTED_GETH_VERSION = "1.10.3"
 LOWEST_SUPPORTED_GETH_VERSION = "1.9.7"
 # this is the last stable version as of this comment
 HIGHEST_SUPPORTED_PARITY_VERSION = "3.1.0"
-LOWEST_SUPPORTED_PARITY_VERSION = "1.7.6"
+LOWEST_SUPPORTED_PARITY_VERSION = "2.7.0"
 
 WEB3_BLOCK_NOT_FOUND_RETRY_COUNT = 3
 
diff --git a/raiden/exceptions.py b/raiden/exceptions.py
index 0edbe90634..3a7e9a03d5 100644
--- a/raiden/exceptions.py
+++ b/raiden/exceptions.py
@@ -141,11 +141,11 @@ class DepositMismatch(RaidenRecoverableError):
 
 
 class InvalidChannelID(RaidenError):
-    """ Raised when the user provided value is not a channel id. """
+    """Raised when the user provided value is not a channel id."""
 
 
 class WithdrawMismatch(RaidenRecoverableError):
-    """ Raised when the requested withdraw is larger than actual channel balance. """
+    """Raised when the requested withdraw is larger than actual channel balance."""
 
 
 class InvalidChecksummedAddress(RaidenError):
@@ -168,11 +168,11 @@ class InvalidBinaryAddress(RaidenValidationError):
 
 
 class InvalidSecret(RaidenError):
-    """ Raised when the user provided value is not a valid secret. """
+    """Raised when the user provided value is not a valid secret."""
 
 
 class InvalidSecretHash(RaidenError):
-    """ Raised when the user provided value is not a valid secrethash. """
+    """Raised when the user provided value is not a valid secrethash."""
 
 
 class InvalidAmount(RaidenError):
@@ -206,27 +206,27 @@ class SamePeerAddress(RaidenError):
 
 
 class UnknownTokenAddress(RaidenError):
-    """ Raised when the token address in unknown. """
+    """Raised when the token address in unknown."""
 
 
 class TokenNotRegistered(RaidenError):
-    """ Raised if there is no token network for token used when opening a channel  """
+    """Raised if there is no token network for token used when opening a channel"""
 
 
 class AlreadyRegisteredTokenAddress(RaidenError):
-    """ Raised when the token address in already registered with the given network. """
+    """Raised when the token address in already registered with the given network."""
 
 
 class InvalidToken(RaidenError):
-    """ Raised if the token does not follow the ERC20 standard. """
+    """Raised if the token does not follow the ERC20 standard."""
 
 
 class MaxTokenNetworkNumberReached(RaidenError):
-    """ Raised if the maximum amount of token networks has been registered. """
+    """Raised if the maximum amount of token networks has been registered."""
 
 
 class InvalidTokenAddress(RaidenError):
-    """ Raised if the token address is invalid. """
+    """Raised if the token address is invalid."""
 
 
 class InvalidTokenNetworkDepositLimit(RaidenError):
@@ -242,7 +242,7 @@ class InvalidChannelParticipantDepositLimit(RaidenError):
 
 
 class EthNodeInterfaceError(RaidenError):
-    """ Raised when the underlying ETH node does not support an rpc interface"""
+    """Raised when the underlying ETH node does not support an rpc interface"""
 
 
 class AddressWithoutCode(RaidenError):
@@ -285,7 +285,7 @@ class InvalidNumberInput(RaidenError):
 
 
 class TransportError(RaidenError):
-    """ Raised when a transport encounters an unexpected error """
+    """Raised when a transport encounters an unexpected error"""
 
 
 class ReplacementTransactionUnderpriced(RaidenError):
@@ -328,11 +328,11 @@ class BrokenPreconditionError(RaidenError):
 
 
 class ServiceRequestFailed(RaidenError):
-    """ Raised when a request to one of the raiden services fails. """
+    """Raised when a request to one of the raiden services fails."""
 
 
 class PFSReturnedError(ServiceRequestFailed):
-    """ The PFS responded with a json message containing an error """
+    """The PFS responded with a json message containing an error"""
 
     def __init__(self, message: str, error_code: int, error_details: Dict[str, Any]) -> None:
         args: List[Any] = [f"{message} (PFS error code: {error_code})"]
@@ -360,7 +360,7 @@ def from_response(cls, response_json: Dict[str, Any]) -> "PFSReturnedError":
 
 
 class ServiceRequestIOURejected(PFSReturnedError):
-    """ Raised when a service request fails due to a problem with the iou. """
+    """Raised when a service request fails due to a problem with the iou."""
 
 
 class UndefinedMediationFee(RaidenError):
@@ -377,19 +377,19 @@ class TokenNetworkDeprecated(RaidenError):
 
 
 class MintFailed(RaidenError):
-    """ Raised when an attempt to mint a testnet token failed. """
+    """Raised when an attempt to mint a testnet token failed."""
 
 
 class SerializationError(RaidenError):
-    """ Invalid data are to be (de-)serialized. """
+    """Invalid data are to be (de-)serialized."""
 
 
 class MatrixSyncMaxTimeoutReached(RaidenRecoverableError):
-    """ Raised if processing the matrix response takes longer than the poll timeout. """
+    """Raised if processing the matrix response takes longer than the poll timeout."""
 
 
 class ConfigurationError(RaidenError):
-    """ Raised when there is something wrong with the provided Raiden Configuration/arguments """
+    """Raised when there is something wrong with the provided Raiden Configuration/arguments"""
 
 
 class UserDepositNotConfigured(RaidenRecoverableError):
diff --git a/raiden/log_config.py b/raiden/log_config.py
index 97765fa2c6..c8f2b9e488 100644
--- a/raiden/log_config.py
+++ b/raiden/log_config.py
@@ -61,7 +61,7 @@ def _match_list(module_rule: Tuple[List[str], str], logger_name: str) -> Tuple[i
 
 
 class LogFilter:
-    """ Utility for filtering log records on module level rules """
+    """Utility for filtering log records on module level rules"""
 
     def __init__(self, config: Dict[str, str], default_level: str):
         """Initializes a new `LogFilter`
@@ -90,7 +90,7 @@ def _get_log_level(self, logger_name: str) -> str:
         return best_match_level
 
     def should_log(self, logger_name: str, level: str) -> bool:
-        """ Returns if a message for the logger should be logged. """
+        """Returns if a message for the logger should be logged."""
         if (logger_name, level) not in self._should_log:
             log_level_per_rule = self._get_log_level(logger_name)
             log_level_per_rule_numeric = getattr(logging, log_level_per_rule.upper(), 10)
diff --git a/raiden/message_handler.py b/raiden/message_handler.py
index b77dc7dafa..0bf487b439 100644
--- a/raiden/message_handler.py
+++ b/raiden/message_handler.py
@@ -4,6 +4,7 @@
 from gevent.pool import Pool
 
 from raiden.constants import ABSENT_SECRET, BLOCK_ID_LATEST
+from raiden.exceptions import ServiceRequestFailed
 from raiden.messages.abstract import Message
 from raiden.messages.decode import balanceproof_from_envelope, lockedtransfersigned_from_message
 from raiden.messages.synchronization import Delivered, Processed
@@ -16,6 +17,7 @@
     Unlock,
 )
 from raiden.messages.withdraw import WithdrawConfirmation, WithdrawExpired, WithdrawRequest
+from raiden.network.pathfinding import query_address_metadata
 from raiden.transfer import views
 from raiden.transfer.architecture import StateChange
 from raiden.transfer.identifiers import CanonicalIdentifier
@@ -41,7 +43,15 @@
 )
 from raiden.transfer.views import TransferRole
 from raiden.utils.transfers import random_secret
-from raiden.utils.typing import TYPE_CHECKING, List, Set, TargetAddress, Tuple
+from raiden.utils.typing import (
+    TYPE_CHECKING,
+    AddressMetadata,
+    List,
+    Optional,
+    Set,
+    TargetAddress,
+    Tuple,
+)
 
 if TYPE_CHECKING:
     from raiden.raiden_service import RaidenService
@@ -113,6 +123,18 @@ def handle_message_withdrawrequest(
         raiden: "RaidenService", message: WithdrawRequest  # pylint: disable=unused-argument
     ) -> List[StateChange]:
         assert message.sender, "message must be signed"
+
+        sender_metadata: Optional[AddressMetadata] = None
+        if raiden.config.pfs_config is not None:
+            # FIXME querying the PFS for the address-metadata directly after receiving a message
+            #   should be optimized / factored out at a later point!
+            try:
+                sender_metadata = query_address_metadata(raiden.config.pfs_config, message.sender)
+            except ServiceRequestFailed as ex:
+                msg = f"PFS returned an error while trying to fetch user information: \n{ex}"
+                log.warning(msg)
+                sender_metadata = None
+
         withdraw_request = ReceiveWithdrawRequest(
             canonical_identifier=CanonicalIdentifier(
                 chain_identifier=message.chain_id,
@@ -126,6 +148,7 @@ def handle_message_withdrawrequest(
             nonce=message.nonce,
             expiration=message.expiration,
             signature=message.signature,
+            sender_metadata=sender_metadata,
         )
         return [withdraw_request]
 
@@ -223,6 +246,9 @@ def handle_message_refundtransfer(
         raiden: "RaidenService", message: RefundTransfer
     ) -> List[StateChange]:
         chain_state = views.state_from_raiden(raiden)
+        # XXX: Not sure about this one. What should we do if the LockedTransfer has no
+        #      route_states due to validation?
+        # AFAIK the routes in the received transfer aren't relevant for the refund anyway.
         from_transfer = lockedtransfersigned_from_message(message=message)
 
         role = views.get_transfer_role(
diff --git a/raiden/messages/abstract.py b/raiden/messages/abstract.py
index 2ee01b380f..32125e976c 100644
--- a/raiden/messages/abstract.py
+++ b/raiden/messages/abstract.py
@@ -42,7 +42,7 @@ class Message:
 
 @dataclass(repr=False, eq=False)
 class AuthenticatedMessage(Message):
-    """ Messages which the sender can be verified. """
+    """Messages which the sender can be verified."""
 
     def sender(self) -> Optional[Address]:
         raise NotImplementedError("Property needs to be implemented in subclass.")
@@ -75,7 +75,7 @@ def _data_to_sign(self) -> bytes:
         raise NotImplementedError
 
     def sign(self, signer: Signer) -> None:
-        """ Sign message using signer. """
+        """Sign message using signer."""
         message_data = self._data_to_sign()
         self.signature = signer.sign(data=message_data)
 
@@ -97,13 +97,13 @@ def sender(self) -> Optional[Address]:  # type: ignore
 
 @dataclass(repr=False, eq=False)
 class RetrieableMessage(Message):
-    """ Message, that supports a retry-queue. """
+    """Message, that supports a retry-queue."""
 
     message_identifier: MessageID
 
 
 @dataclass(repr=False, eq=False)
 class SignedRetrieableMessage(SignedMessage, RetrieableMessage):
-    """ Mixin of SignedMessage and RetrieableMessage. """
+    """Mixin of SignedMessage and RetrieableMessage."""
 
     pass
diff --git a/raiden/messages/decode.py b/raiden/messages/decode.py
index 9022c7e9f6..2d8eae9318 100644
--- a/raiden/messages/decode.py
+++ b/raiden/messages/decode.py
@@ -1,9 +1,13 @@
+from structlog import get_logger
+
 from raiden.messages.transfers import EnvelopeMessage, LockedTransferBase
 from raiden.transfer.identifiers import CanonicalIdentifier
 from raiden.transfer.mediated_transfer.state import LockedTransferSignedState, RouteState
 from raiden.transfer.state import BalanceProofSignedState, HashTimeLockState
 from raiden.utils.typing import AdditionalHash
 
+log = get_logger(__name__)
+
 
 def balanceproof_from_envelope(envelope_message: EnvelopeMessage) -> BalanceProofSignedState:
     assert envelope_message.sender, "envelope_message must be signed"
@@ -24,14 +28,19 @@ def balanceproof_from_envelope(envelope_message: EnvelopeMessage) -> BalanceProo
 
 
 def lockedtransfersigned_from_message(message: LockedTransferBase) -> LockedTransferSignedState:
-    """ Create LockedTransferSignedState from a LockedTransfer message. """
+    """Create LockedTransferSignedState from a LockedTransfer message."""
     balance_proof = balanceproof_from_envelope(message)
 
     lock = HashTimeLockState(message.lock.amount, message.lock.expiration, message.lock.secrethash)
     route_states = list()
     for route_metadata in message.metadata.routes:
-        rs = RouteState(route_metadata.route, address_to_metadata=route_metadata.address_metadata)
-        route_states.append(rs)
+        try:
+            rs = RouteState(
+                route_metadata.route, address_to_metadata=route_metadata.address_metadata or {}
+            )
+            route_states.append(rs)
+        except ValueError as ex:
+            log.warning("Invalid metadata in received route", route=route_metadata, error=str(ex))
 
     transfer_state = LockedTransferSignedState(
         message_identifier=message.message_identifier,
diff --git a/raiden/messages/encode.py b/raiden/messages/encode.py
index e3c023f70b..a18c9c3448 100644
--- a/raiden/messages/encode.py
+++ b/raiden/messages/encode.py
@@ -3,7 +3,6 @@
 from raiden.messages.transfers import (
     LockedTransfer,
     LockExpired,
-    RefundTransfer,
     RevealSecret,
     SecretRequest,
     Unlock,
@@ -19,7 +18,6 @@
 from raiden.transfer.mediated_transfer.events import (
     SendLockedTransfer,
     SendLockExpired,
-    SendRefundTransfer,
     SendSecretRequest,
     SendSecretReveal,
     SendUnlock,
@@ -40,9 +38,6 @@ def message_from_sendevent(send_event: SendMessageEvent) -> Message:
     elif type(send_event) == SendSecretRequest:
         assert isinstance(send_event, SendSecretRequest), MYPY_ANNOTATION
         return SecretRequest.from_event(send_event)
-    elif type(send_event) == SendRefundTransfer:
-        assert isinstance(send_event, SendRefundTransfer), MYPY_ANNOTATION
-        return RefundTransfer.from_event(send_event)
     elif type(send_event) == SendLockExpired:
         assert isinstance(send_event, SendLockExpired), MYPY_ANNOTATION
         return LockExpired.from_event(send_event)
diff --git a/raiden/messages/healthcheck.py b/raiden/messages/healthcheck.py
index ff6f0788ad..928209b58d 100644
--- a/raiden/messages/healthcheck.py
+++ b/raiden/messages/healthcheck.py
@@ -32,7 +32,7 @@ def _data_to_sign(self) -> bytes:
 
 @dataclass(repr=False, eq=False)
 class Pong(SignedMessage):
-    """ Response to a Ping message. """
+    """Response to a Ping message."""
 
     cmdid: ClassVar[CmdId] = CmdId.PONG
 
diff --git a/raiden/messages/metadata.py b/raiden/messages/metadata.py
index f7d0120a70..31a8d6d403 100644
--- a/raiden/messages/metadata.py
+++ b/raiden/messages/metadata.py
@@ -1,32 +1,51 @@
-from dataclasses import dataclass, field
+from copy import deepcopy
+from dataclasses import dataclass
 
 import canonicaljson
-import rlp
 from eth_utils import keccak
 
 from raiden.messages.abstract import cached_property
 from raiden.utils.formatting import to_checksum_address
-from raiden.utils.typing import Address, AddressMetadata, Dict, List
+from raiden.utils.typing import MYPY_ANNOTATION, Address, AddressMetadata, Dict, List, Optional
+from raiden.utils.validation import validate_address_metadata
 
 
-@dataclass(frozen=True)
 class RouteMetadata:
     route: List[Address]
-    address_metadata: Dict[Address, AddressMetadata] = field(default_factory=dict)
+    address_metadata: Optional[Dict[Address, AddressMetadata]]
 
-    @cached_property
-    def hash(self) -> bytes:
-        return keccak(self._serialize_canonicaljson())
+    def __init__(
+        self,
+        route: List[Address],
+        address_metadata: Optional[Dict[Address, AddressMetadata]] = None,
+    ) -> None:
+
+        self.address_metadata = deepcopy(address_metadata) or {}
+        self.route = route
+        self._validate_address_metadata()
+
+    def _validate_address_metadata(self) -> None:
+        assert self.address_metadata is not None, MYPY_ANNOTATION
+        validation_errors = validate_address_metadata(self)
+        for address in validation_errors:
+            del self.address_metadata[address]
+
+    def _canonical_dict(self) -> dict:
+        """Return a dict that can be dumped as json
 
-    def _serialize_canonicaljson(self) -> bytes:
+        Used to build signatures.
+        """
         route = [to_checksum_address(address) for address in self.route]
+        if not self.address_metadata:
+            # We add a default value of {} when validating. Normalize this to
+            # no key when signing.
+            return {"route": route}
+
         address_metadata = {
             to_checksum_address(address): metadata
             for address, metadata in self.address_metadata.items()
         }
-        return canonicaljson.encode_canonical_json(
-            {"route": route, "address_metadata": address_metadata}
-        )
+        return {"route": route, "address_metadata": address_metadata}
 
     def __repr__(self) -> str:
         return f"RouteMetadata: {' -> '.join([to_checksum_address(a) for a in self.route])}"
@@ -38,7 +57,12 @@ class Metadata:
 
     @cached_property
     def hash(self) -> bytes:
-        return keccak(rlp.encode([r.hash for r in self.routes]))
+        return keccak(self._serialize_canonical())
 
     def __repr__(self) -> str:
         return f"Metadata: routes: {[repr(route) for route in self.routes]}"
+
+    def _serialize_canonical(self) -> bytes:
+        return canonicaljson.encode_canonical_json(
+            {"routes": [route._canonical_dict() for route in self.routes]}
+        )
diff --git a/raiden/messages/monitoring_service.py b/raiden/messages/monitoring_service.py
index ba56a0a0ef..13a73ab896 100644
--- a/raiden/messages/monitoring_service.py
+++ b/raiden/messages/monitoring_service.py
@@ -132,7 +132,7 @@ def reward_proof_signature(self) -> Optional[Signature]:
         return self.signature
 
     def _data_to_sign(self) -> bytes:
-        """ Return the binary data to be/which was signed """
+        """Return the binary data to be/which was signed"""
         assert self.non_closing_signature is not None, "Message is not signed yet."
         packed = pack_reward_proof(
             chain_id=self.balance_proof.chain_id,
diff --git a/raiden/messages/path_finding_service.py b/raiden/messages/path_finding_service.py
index 02399018cc..ff07f6180a 100644
--- a/raiden/messages/path_finding_service.py
+++ b/raiden/messages/path_finding_service.py
@@ -16,7 +16,7 @@
 
 @dataclass(repr=False, eq=False)
 class PFSCapacityUpdate(SignedMessage):
-    """ Message to inform a pathfinding service about a capacity change. """
+    """Message to inform a pathfinding service about a capacity change."""
 
     canonical_identifier: CanonicalIdentifier
     updating_participant: Address
diff --git a/raiden/messages/transfers.py b/raiden/messages/transfers.py
index fa074a069b..3a5eb65852 100644
--- a/raiden/messages/transfers.py
+++ b/raiden/messages/transfers.py
@@ -1,6 +1,6 @@
 from dataclasses import dataclass, field
 from hashlib import sha256
-from typing import Any, overload
+from typing import Any
 
 import eth_hash.auto as eth_hash
 from eth_utils import keccak
@@ -11,9 +11,7 @@
 from raiden.messages.metadata import Metadata, RouteMetadata
 from raiden.transfer.identifiers import CanonicalIdentifier
 from raiden.transfer.mediated_transfer.events import (
-    SendLockedTransfer,
     SendLockExpired,
-    SendRefundTransfer,
     SendSecretRequest,
     SendSecretReveal,
     SendUnlock,
@@ -201,7 +199,7 @@ def _data_to_sign(self) -> bytes:
 
 @dataclass(repr=False, eq=False)
 class SecretRequest(SignedRetrieableMessage):
-    """ Requests the secret/preimage which unlocks a lock. """
+    """Requests the secret/preimage which unlocks a lock."""
 
     cmdid: ClassVar[CmdId] = CmdId.SECRETREQUEST
 
@@ -367,18 +365,6 @@ def __post_init__(self) -> None:
         if len(self.initiator) != 20:
             raise ValueError("initiator is an invalid address")
 
-    @overload
-    @classmethod
-    def from_event(cls, event: SendLockedTransfer) -> "LockedTransfer":
-        # pylint: disable=unused-argument
-        ...
-
-    @overload  # noqa: F811
-    @classmethod
-    def from_event(cls, event: SendRefundTransfer) -> "RefundTransfer":  # noqa: F811
-        # pylint: disable=unused-argument
-        ...
-
     @classmethod  # noqa: F811
     def from_event(cls, event: Any) -> Any:  # noqa: F811
         transfer = event.transfer
diff --git a/raiden/messages/withdraw.py b/raiden/messages/withdraw.py
index c29389ae52..e1c7832e7c 100644
--- a/raiden/messages/withdraw.py
+++ b/raiden/messages/withdraw.py
@@ -23,7 +23,7 @@
 
 @dataclass(repr=False, eq=False)
 class WithdrawRequest(SignedRetrieableMessage):
-    """ Requests a signed on-chain withdraw confirmation from partner. """
+    """Requests a signed on-chain withdraw confirmation from partner."""
 
     cmdid: ClassVar[CmdId] = CmdId.WITHDRAW_REQUEST
     message_type: ClassVar[int] = MessageTypeId.WITHDRAW
@@ -64,7 +64,7 @@ def _data_to_sign(self) -> bytes:
 
 @dataclass(repr=False, eq=False)
 class WithdrawConfirmation(SignedRetrieableMessage):
-    """ Confirms withdraw to partner with a signature """
+    """Confirms withdraw to partner with a signature"""
 
     cmdid: ClassVar[CmdId] = CmdId.WITHDRAW_CONFIRMATION
     message_type: ClassVar[int] = MessageTypeId.WITHDRAW
@@ -105,7 +105,7 @@ def _data_to_sign(self) -> bytes:
 
 @dataclass(eq=False)
 class WithdrawExpired(SignedRetrieableMessage):
-    """ Notifies about withdraw expiration/cancellation from partner. """
+    """Notifies about withdraw expiration/cancellation from partner."""
 
     cmdid: ClassVar[CmdId] = CmdId.WITHDRAW_EXPIRED
     message_type: ClassVar[int] = MessageTypeId.WITHDRAW
diff --git a/raiden/network/pathfinding.py b/raiden/network/pathfinding.py
index 4d6b23bd4e..830f7d48c8 100644
--- a/raiden/network/pathfinding.py
+++ b/raiden/network/pathfinding.py
@@ -546,7 +546,7 @@ def query_address_metadata(
     pfs_config: PFSConfig,
     user_address: Union[Address, TargetAddress],
 ) -> AddressMetadata:
-    """ Get the matrix user_id for the given address from the PFS """
+    """Get the matrix user_id for the given address from the PFS"""
     try:
         response = session.get(
             f"{pfs_config.info.url}/api/v1/address/{to_checksum_address(user_address)}/metadata",
diff --git a/raiden/network/proxies/payment_channel.py b/raiden/network/proxies/payment_channel.py
index 98f031b91c..c5c9cc6017 100644
--- a/raiden/network/proxies/payment_channel.py
+++ b/raiden/network/proxies/payment_channel.py
@@ -36,11 +36,11 @@ def __init__(
         self.contract_manager = contract_manager
 
     def token_address(self) -> TokenAddress:
-        """ Returns the address of the token for the channel. """
+        """Returns the address of the token for the channel."""
         return self.token_network.token_address()
 
     def detail(self, block_identifier: BlockIdentifier) -> ChannelDetails:
-        """ Returns the channel details. """
+        """Returns the channel details."""
         return self.token_network.detail(
             participant1=self.participant1,
             participant2=self.participant2,
@@ -49,7 +49,7 @@ def detail(self, block_identifier: BlockIdentifier) -> ChannelDetails:
         )
 
     def settle_timeout(self) -> BlockTimeout:
-        """ Returns the channels settle_timeout. """
+        """Returns the channels settle_timeout."""
 
         # There is no way to get the settle timeout after the channel has been closed as
         # we're saving gas. Therefore get the ChannelOpened event and get the timeout there.
@@ -70,7 +70,7 @@ def settle_timeout(self) -> BlockTimeout:
         return event["args"]["settle_timeout"]
 
     def opened(self, block_identifier: BlockIdentifier) -> bool:
-        """ Returns if the channel is opened. """
+        """Returns if the channel is opened."""
         return self.token_network.channel_is_opened(
             participant1=self.participant1,
             participant2=self.participant2,
@@ -79,7 +79,7 @@ def opened(self, block_identifier: BlockIdentifier) -> bool:
         )
 
     def closed(self, block_identifier: BlockIdentifier) -> bool:
-        """ Returns if the channel is closed. """
+        """Returns if the channel is closed."""
         return self.token_network.channel_is_closed(
             participant1=self.participant1,
             participant2=self.participant2,
@@ -88,7 +88,7 @@ def closed(self, block_identifier: BlockIdentifier) -> bool:
         )
 
     def settled(self, block_identifier: BlockIdentifier) -> bool:
-        """ Returns if the channel is settled. """
+        """Returns if the channel is settled."""
         return self.token_network.channel_is_settled(
             participant1=self.participant1,
             participant2=self.participant2,
@@ -97,7 +97,7 @@ def settled(self, block_identifier: BlockIdentifier) -> bool:
         )
 
     def can_transfer(self, block_identifier: BlockIdentifier) -> bool:
-        """ Returns True if the channel is opened and the node has deposit in it. """
+        """Returns True if the channel is opened and the node has deposit in it."""
         return self.token_network.can_transfer(
             participant1=self.participant1,
             participant2=self.participant2,
@@ -143,7 +143,7 @@ def close(
         closing_signature: Signature,
         block_identifier: BlockIdentifier,
     ) -> None:
-        """ Closes the channel using the provided balance proof, and our closing signature. """
+        """Closes the channel using the provided balance proof, and our closing signature."""
         self.token_network.close(
             channel_identifier=self.channel_identifier,
             partner=self.participant2,
@@ -164,7 +164,7 @@ def update_transfer(
         signature: Signature,
         block_identifier: BlockIdentifier,
     ) -> None:
-        """ Updates the channel using the provided balance proof. """
+        """Updates the channel using the provided balance proof."""
         self.token_network.update_transfer(
             channel_identifier=self.channel_identifier,
             partner=self.participant2,
@@ -201,7 +201,7 @@ def settle(
         partner_locksroot: Locksroot,
         block_identifier: BlockIdentifier,
     ) -> TransactionHash:
-        """ Settles the channel. """
+        """Settles the channel."""
         return self.token_network.settle(
             channel_identifier=self.channel_identifier,
             transferred_amount=transferred_amount,
diff --git a/raiden/network/proxies/proxy_manager.py b/raiden/network/proxies/proxy_manager.py
index 3a4bd725d0..42c4ca1464 100644
--- a/raiden/network/proxies/proxy_manager.py
+++ b/raiden/network/proxies/proxy_manager.py
@@ -112,7 +112,7 @@ def __init__(
         self._one_to_n_creation_lock = Semaphore()
 
     def token(self, token_address: TokenAddress, block_identifier: BlockIdentifier) -> Token:
-        """ Return a proxy to interact with a token. """
+        """Return a proxy to interact with a token."""
         if not is_binary_address(token_address):
             raise ValueError("token_address must be a valid address")
 
@@ -130,7 +130,7 @@ def token(self, token_address: TokenAddress, block_identifier: BlockIdentifier)
     def custom_token(
         self, token_address: TokenAddress, block_identifier: BlockIdentifier
     ) -> CustomToken:
-        """ Return a proxy to interact with a token. """
+        """Return a proxy to interact with a token."""
         if not is_binary_address(token_address):
             raise ValueError("token_address must be a valid address")
 
diff --git a/raiden/network/proxies/secret_registry.py b/raiden/network/proxies/secret_registry.py
index d57e798e2d..cefa7b88c2 100644
--- a/raiden/network/proxies/secret_registry.py
+++ b/raiden/network/proxies/secret_registry.py
@@ -7,6 +7,7 @@
 from gevent.lock import Semaphore
 
 from raiden.constants import (
+    BLOCK_ID_PENDING,
     GAS_REQUIRED_PER_SECRET_IN_BATCH,
     GAS_REQUIRED_REGISTER_SECRET_BATCH_BASE,
 )
@@ -274,7 +275,7 @@ def _register_secret_batch(
                 transaction_name="registerSecretBatch",
                 transaction_executed=True,
                 required_gas=GAS_REQUIRED_PER_SECRET_IN_BATCH * len(secrets_to_register),
-                block_identifier=self.client.get_checking_block(),
+                block_identifier=BLOCK_ID_PENDING,
             )
             error = "Call to registerSecretBatch couldn't be done"
 
diff --git a/raiden/network/proxies/token.py b/raiden/network/proxies/token.py
index d064124db5..c690408f67 100644
--- a/raiden/network/proxies/token.py
+++ b/raiden/network/proxies/token.py
@@ -162,7 +162,7 @@ def approve(self, allowed_address: Address, allowance: TokenAmount) -> Transacti
     def balance_of(
         self, address: Address, block_identifier: BlockIdentifier = BLOCK_ID_LATEST
     ) -> Balance:
-        """ Return the balance of `address`. """
+        """Return the balance of `address`."""
         return self.proxy.functions.balanceOf(address).call(block_identifier=block_identifier)
 
     def total_supply(
diff --git a/raiden/network/proxies/token_network.py b/raiden/network/proxies/token_network.py
index 309a307374..56c2e58563 100644
--- a/raiden/network/proxies/token_network.py
+++ b/raiden/network/proxies/token_network.py
@@ -188,15 +188,15 @@ def __init__(
         self.opening_channels_count = 0
 
     def chain_id(self) -> ChainID:
-        """ Return the token of this manager. """
+        """Return the token of this manager."""
         return self._chain_id
 
     def token_address(self) -> TokenAddress:
-        """ Return the token of this manager. """
+        """Return the token of this manager."""
         return self._token_address
 
     def channel_participant_deposit_limit(self, block_identifier: BlockIdentifier) -> TokenAmount:
-        """ Return the deposit limit of a channel participant. """
+        """Return the deposit limit of a channel participant."""
         return TokenAmount(
             self.proxy.functions.channel_participant_deposit_limit().call(
                 block_identifier=block_identifier
@@ -204,7 +204,7 @@ def channel_participant_deposit_limit(self, block_identifier: BlockIdentifier) -
         )
 
     def token_network_deposit_limit(self, block_identifier: BlockIdentifier) -> TokenAmount:
-        """ Return the token of this manager. """
+        """Return the token of this manager."""
         return TokenAmount(
             self.proxy.functions.token_network_deposit_limit().call(
                 block_identifier=block_identifier
@@ -416,7 +416,7 @@ def get_channel_identifier(
     def get_channel_identifier_or_none(
         self, participant1: Address, participant2: Address, block_identifier: BlockIdentifier
     ) -> Optional[ChannelID]:
-        """ Returns the channel identifier if an open channel exists, else None. """
+        """Returns the channel identifier if an open channel exists, else None."""
         try:
             return self.get_channel_identifier(
                 participant1=participant1,
@@ -433,7 +433,7 @@ def _detail_participant(
         partner: Address,
         block_identifier: BlockIdentifier,
     ) -> ParticipantDetails:
-        """ Returns a dictionary with the channel participant information. """
+        """Returns a dictionary with the channel participant information."""
         raise_if_invalid_address_pair(detail_for, partner)
 
         data = self.proxy.functions.getChannelParticipantInfo(
@@ -578,11 +578,11 @@ def detail(
         )
 
     def settlement_timeout_min(self) -> int:
-        """ Returns the minimal settlement timeout for the token network. """
+        """Returns the minimal settlement timeout for the token network."""
         return self.proxy.functions.settlement_timeout_min().call()
 
     def settlement_timeout_max(self) -> int:
-        """ Returns the maximal settlement timeout for the token network. """
+        """Returns the maximal settlement timeout for the token network."""
         return self.proxy.functions.settlement_timeout_max().call()
 
     def channel_is_opened(
@@ -592,7 +592,7 @@ def channel_is_opened(
         block_identifier: BlockIdentifier,
         channel_identifier: ChannelID,
     ) -> bool:
-        """ Returns true if the channel is in an open state, false otherwise. """
+        """Returns true if the channel is in an open state, false otherwise."""
         try:
             channel_data = self._detail_channel(
                 participant1=participant1,
@@ -611,7 +611,7 @@ def channel_is_closed(
         block_identifier: BlockIdentifier,
         channel_identifier: ChannelID,
     ) -> bool:
-        """ Returns true if the channel is in a closed state, false otherwise. """
+        """Returns true if the channel is in a closed state, false otherwise."""
         try:
             channel_data = self._detail_channel(
                 participant1=participant1,
@@ -630,7 +630,7 @@ def channel_is_settled(
         block_identifier: BlockIdentifier,
         channel_identifier: ChannelID,
     ) -> bool:
-        """ Returns true if the channel is in a settled state, false otherwise. """
+        """Returns true if the channel is in a settled state, false otherwise."""
         try:
             channel_data = self._detail_channel(
                 participant1=participant1,
diff --git a/raiden/network/proxies/token_network_registry.py b/raiden/network/proxies/token_network_registry.py
index 96ce971898..fed2bbbf12 100644
--- a/raiden/network/proxies/token_network_registry.py
+++ b/raiden/network/proxies/token_network_registry.py
@@ -548,13 +548,13 @@ def get_deprecation_executor(self, block_identifier: BlockIdentifier) -> Address
         )
 
     def settlement_timeout_min(self, block_identifier: BlockIdentifier) -> int:
-        """ Returns the minimal settlement timeout for the token network registry. """
+        """Returns the minimal settlement timeout for the token network registry."""
         return self.proxy.functions.settlement_timeout_min().call(
             block_identifier=block_identifier
         )
 
     def settlement_timeout_max(self, block_identifier: BlockIdentifier) -> int:
-        """ Returns the maximal settlement timeout for the token network registry. """
+        """Returns the maximal settlement timeout for the token network registry."""
         return self.proxy.functions.settlement_timeout_max().call(
             block_identifier=block_identifier
         )
diff --git a/raiden/network/proxies/user_deposit.py b/raiden/network/proxies/user_deposit.py
index 02e8180274..1e82607804 100644
--- a/raiden/network/proxies/user_deposit.py
+++ b/raiden/network/proxies/user_deposit.py
@@ -7,7 +7,7 @@
 from gevent.threading import Lock
 from web3.exceptions import BadFunctionCallOutput
 
-from raiden.constants import BLOCK_ID_LATEST, EMPTY_ADDRESS, UINT256_MAX
+from raiden.constants import BLOCK_ID_LATEST, BLOCK_ID_PENDING, EMPTY_ADDRESS, UINT256_MAX
 from raiden.exceptions import BrokenPreconditionError, RaidenRecoverableError
 from raiden.network.proxies.token import Token
 from raiden.network.proxies.utils import raise_on_call_returned_empty
@@ -167,7 +167,7 @@ def init(
         one_to_n_address: OneToNAddress,
         given_block_identifier: BlockIdentifier,
     ) -> TransactionHash:
-        """ Initialize the UserDeposit contract with MS and OneToN addresses """
+        """Initialize the UserDeposit contract with MS and OneToN addresses"""
         check_address_has_code_handle_pruned_block(
             client=self.client,
             address=Address(monitoring_service_address),
@@ -287,7 +287,7 @@ def _init(
             return transaction_mined.transaction_hash
 
     def effective_balance(self, address: Address, block_identifier: BlockIdentifier) -> Balance:
-        """ The user's balance with planned withdrawals deducted. """
+        """The user's balance with planned withdrawals deducted."""
         balance = self.proxy.functions.effectiveBalance(address).call(
             block_identifier=block_identifier
         )
@@ -303,7 +303,7 @@ def deposit(
         total_deposit: TokenAmount,
         given_block_identifier: BlockIdentifier,
     ) -> TransactionHash:
-        """ Increase the total deposit of the beneficiary's account to `total_deposit`. """
+        """Increase the total deposit of the beneficiary's account to `total_deposit`."""
 
         token_address = self.token_address(given_block_identifier)
         token = self.proxy_manager.token(
@@ -426,7 +426,7 @@ def plan_withdraw(
     def withdraw(
         self, amount: TokenAmount, given_block_identifier: BlockIdentifier
     ) -> TransactionHash:
-        """ Withdraw tokens from UDC, requires a mature withdraw plan"""
+        """Withdraw tokens from UDC, requires a mature withdraw plan"""
 
         token_address = self.token_address(given_block_identifier)
         token = self.proxy_manager.token(
@@ -476,7 +476,7 @@ def _deposit_preconditions(
             # precondition checks but must still set the amount_to_deposit to a
             # reasonable value.
             previous_total_deposit = self.get_total_deposit(
-                address=beneficiary, block_identifier=self.client.get_checking_block()
+                address=beneficiary, block_identifier=BLOCK_ID_PENDING
             )
             amount_to_deposit = TokenAmount(total_deposit - previous_total_deposit)
         except BadFunctionCallOutput:
@@ -690,7 +690,7 @@ def _deposit_check_result(
     def _plan_withdraw_preconditions(
         self, amount_to_plan_withdraw: TokenAmount, given_block_identifier: BlockIdentifier
     ) -> None:
-        """ Check if a WithdrawPlan for the given amount can be created. """
+        """Check if a WithdrawPlan for the given amount can be created."""
         if amount_to_plan_withdraw <= 0:
             raise BrokenPreconditionError("Planned withdraw amount must be greater than zero.")
 
diff --git a/raiden/network/rpc/client.py b/raiden/network/rpc/client.py
index 7b4269f69a..78205aeaeb 100644
--- a/raiden/network/rpc/client.py
+++ b/raiden/network/rpc/client.py
@@ -106,7 +106,7 @@ def logs_blocks_sanity_check(from_block: BlockIdentifier, to_block: BlockIdentif
 
 
 def check_transaction_failure(transaction: "TransactionMined", client: "JSONRPCClient") -> None:
-    """ Raise an exception if the transaction consumed all the gas. """
+    """Raise an exception if the transaction consumed all the gas."""
 
     if was_transaction_successfully_mined(transaction):
         return
@@ -216,7 +216,7 @@ def check_transaction_failure(transaction: "TransactionMined", client: "JSONRPCC
 
 
 def was_transaction_successfully_mined(transaction: "TransactionMined") -> bool:
-    """ `True` if the transaction was successfully mined, `False` otherwise. """
+    """`True` if the transaction was successfully mined, `False` otherwise."""
     if "status" not in transaction.receipt:
         # This should never happen. Raiden checks ethereum client for compatibility at startup
         raise AssertionError(
@@ -579,11 +579,6 @@ def is_infura(web3: Web3) -> bool:
 def patched_web3_eth_estimate_gas(
     self: Any, transaction: TxParams, block_identifier: BlockIdentifier = None
 ) -> Wei:
-    """Temporary workaround until next web3.py release (5.X.X)
-
-    Current master of web3.py has this implementation already:
-    https://github.com/ethereum/web3.py/blob/2a67ea9f0ab40bb80af2b803dce742d6cad5943e/web3/eth.py#L311
-    """
     if "from" not in transaction and is_checksum_address(self.default_account):
         transaction = assoc(transaction, "from", self.default_account)
 
@@ -667,7 +662,6 @@ def estimate_gas_for_function(
 def patched_contractfunction_estimateGas(
     self: Any, transaction: TxParams = None, block_identifier: BlockIdentifier = None
 ) -> int:
-    """Temporary workaround until next web3.py release (5.X.X)"""
     if transaction is None:
         estimate_gas_transaction: TxParams = {}
     else:
@@ -708,7 +702,7 @@ def patched_contractfunction_estimateGas(
 def make_sane_poa_middleware(
     make_request: Callable[[RPCEndpoint, Any], Any], web3: Web3  # pylint: disable=unused-argument
 ) -> Callable[[RPCEndpoint, Any], RPCResponse]:
-    """ Simpler geth_poa_middleware that doesn't break with ``null`` responses. """
+    """Simpler geth_poa_middleware that doesn't break with ``null`` responses."""
 
     def middleware(method: RPCEndpoint, params: Any) -> RPCResponse:
         response = make_request(method, params)
@@ -786,7 +780,8 @@ def monkey_patch_web3(web3: Web3, gas_price_strategy: Callable) -> None:
         # scoped web3 instance is used for all clients
         pass
 
-    # Temporary until next web3.py release (5.X.X)
+    # We need these so that we can check for exceptions that are expected.
+    # See the docstring of check_value_error for details.
     ContractFunction.estimateGas = patched_contractfunction_estimateGas  # type: ignore
     Eth.estimateGas = patched_web3_eth_estimate_gas  # type: ignore
 
@@ -1022,7 +1017,7 @@ class TransactionMined:
 
 
 class JSONRPCClient:
-    """ Ethereum JSON RPC client. """
+    """Ethereum JSON RPC client."""
 
     def __init__(
         self,
@@ -1077,7 +1072,7 @@ def __repr__(self) -> str:
         )
 
     def block_number(self) -> BlockNumber:
-        """ Return the most recent block. """
+        """Return the most recent block."""
         return self.web3.eth.block_number
 
     def get_block(self, block_identifier: BlockIdentifier) -> BlockData:
@@ -1085,7 +1080,7 @@ def get_block(self, block_identifier: BlockIdentifier) -> BlockData:
         return self.web3.eth.getBlock(block_identifier)
 
     def get_confirmed_blockhash(self) -> BlockHash:
-        """ Gets the block CONFIRMATION_BLOCKS in the past and returns its block hash """
+        """Gets the block CONFIRMATION_BLOCKS in the past and returns its block hash"""
         confirmed_block_number = BlockNumber(
             self.web3.eth.block_number - self.default_block_num_confirmations
         )
@@ -1114,7 +1109,7 @@ def can_query_state_for_block(self, block_identifier: BlockIdentifier) -> bool:
 
     # FIXME: shouldn't return `TokenAmount`
     def balance(self, account: Address) -> TokenAmount:
-        """ Return the balance of the account of the given address. """
+        """Return the balance of the account of the given address."""
         return TokenAmount(self.web3.eth.get_balance(account, BLOCK_ID_PENDING))
 
     def parity_get_pending_transaction_hash_by_nonce(
@@ -1155,7 +1150,7 @@ def estimate_gas(
             eth_node=self.eth_node,
             extra_log_details=extra_log_details,
         )
-        return pending.estimate_gas(self.get_checking_block())
+        return pending.estimate_gas(BLOCK_ID_PENDING)
 
     def transact(self, transaction: Union[TransactionEstimated, EthTransfer]) -> TransactionSent:
         """Allocates an unique `nonce` and send the transaction to the blockchain.
@@ -1558,7 +1553,7 @@ def get_filter_events(
         from_block: BlockIdentifier = GENESIS_BLOCK_NUMBER,
         to_block: BlockIdentifier = BLOCK_ID_LATEST,
     ) -> List[LogReceipt]:
-        """ Get events for the given query. """
+        """Get events for the given query."""
         logs_blocks_sanity_check(from_block, to_block)
         return self.web3.eth.getLogs(
             FilterParams(
@@ -1599,18 +1594,6 @@ def check_for_insufficient_eth(
             log.critical(msg, required_wei=required_balance, actual_wei=balance)
             raise InsufficientEth(msg)
 
-    def get_checking_block(self) -> BlockIdentifier:
-        """Workaround for parity https://github.com/paritytech/parity-ethereum/issues/9707
-        In parity doing any call() with the 'pending' block no longer falls back
-        to the latest if no pending block is found but throws a mistaken error.
-        Until that bug is fixed we need to enforce special behaviour for parity
-        and use the latest block for checking.
-        """
-        checking_block: BlockIdentifier = BLOCK_ID_PENDING
-        if self.eth_node is EthClient.PARITY:
-            checking_block = BLOCK_ID_LATEST
-        return checking_block
-
     def wait_until_block(
         self, target_block_number: BlockNumber, retry_timeout: float = 0.5
     ) -> BlockNumber:
diff --git a/raiden/network/transport/matrix/__init__.py b/raiden/network/transport/matrix/__init__.py
index e83b423a18..1f3fd6f8d0 100644
--- a/raiden/network/transport/matrix/__init__.py
+++ b/raiden/network/transport/matrix/__init__.py
@@ -2,7 +2,6 @@
 from raiden.network.transport.matrix.utils import (  # noqa
     AddressReachability,
     UserPresence,
-    join_broadcast_room,
     login,
     make_client,
     make_room_alias,
diff --git a/raiden/network/transport/matrix/client.py b/raiden/network/transport/matrix/client.py
index 7160c88c3f..8f5cd7a1bf 100644
--- a/raiden/network/transport/matrix/client.py
+++ b/raiden/network/transport/matrix/client.py
@@ -13,6 +13,7 @@
 from gevent import Greenlet
 from gevent.event import Event
 from gevent.lock import Semaphore
+from gevent.pool import Pool
 from matrix_client.api import MatrixHttpApi
 from matrix_client.client import CACHE, MatrixClient
 from matrix_client.errors import MatrixHttpLibError, MatrixRequestError
@@ -48,7 +49,7 @@ def node_address_from_userid(user_id: Optional[str]) -> Optional[AddressHex]:
 
 
 class Room(MatrixRoom):
-    """ Matrix `Room` subclass that invokes listener callbacks in separate greenlets """
+    """Matrix `Room` subclass that invokes listener callbacks in separate greenlets"""
 
     def __init__(self, client: "GMatrixClient", room_id: str) -> None:
         super().__init__(client, room_id)
@@ -57,7 +58,7 @@ def __init__(self, client: "GMatrixClient", room_id: str) -> None:
         self.canonical_alias: str
 
     def get_joined_members(self, force_resync: bool = False) -> List[User]:
-        """ Return a list of members of this room. """
+        """Return a list of members of this room."""
         if force_resync:
             response = self.client.api.get_room_members(self.room_id)
             for event in response["chunk"]:
@@ -70,7 +71,7 @@ def get_joined_members(self, force_resync: bool = False) -> List[User]:
         return list(self._members.values())
 
     def leave(self) -> None:
-        """ Leave the room. Overriding Matrix method to always return error when request. """
+        """Leave the room. Overriding Matrix method to always return error when request."""
         self.client.api.leave_room(self.room_id)
         self.client.rooms.pop(self.room_id, None)
 
@@ -205,7 +206,7 @@ def _record_server_ident(
         self.server_ident = response.headers.get("Server")
 
     def get_room_state_type(self, room_id: str, event_type: str, state_key: str) -> Dict[str, Any]:
-        """ Perform GET /rooms/$room_id/state/$event_type/$state_key """
+        """Perform GET /rooms/$room_id/state/$event_type/$state_key"""
         return self._send("GET", f"/rooms/{room_id}/state/{event_type}/{state_key}")
 
     def create_room(
@@ -254,7 +255,7 @@ def disable_push_notifications(self) -> Dict[str, Any]:
 
 
 class GMatrixClient(MatrixClient):
-    """ Gevent-compliant MatrixClient subclass """
+    """Gevent-compliant MatrixClient subclass"""
 
     sync_worker: Optional[Greenlet] = None
     message_worker: Optional[Greenlet] = None
@@ -263,7 +264,6 @@ class GMatrixClient(MatrixClient):
     def __init__(
         self,
         handle_messages_callback: Callable[[MatrixSyncMessages], bool],
-        handle_member_join_callback: Callable[[Room], None],
         base_url: str,
         token: str = None,
         user_id: str = None,
@@ -280,7 +280,6 @@ def __init__(
         self.token: Optional[str] = None
         self.environment = environment
         self.handle_messages_callback = handle_messages_callback
-        self._handle_member_join_callback = handle_member_join_callback
         self.response_queue: NotifyingQueue[Tuple[UUID, JSONResponse, datetime]] = NotifyingQueue()
         self.stop_event = Event()
 
@@ -300,7 +299,7 @@ def __init__(
 
         # Monotonically increasing id to ensure that presence updates are processed in order.
         self._presence_update_ids: Iterator[int] = itertools.count()
-        self._worker_pool = gevent.pool.Pool(size=20)
+        self._worker_pool = Pool(size=20)
         # Gets incremented every time a sync loop is completed. This is useful since the sync token
         # can remain constant over multiple loops (if no events occur).
         self.sync_progress = SyncProgress(self.response_queue)
@@ -477,7 +476,7 @@ def start_listener_thread(
         self.stop_event.clear()
 
     def stop_listener_thread(self) -> None:
-        """ Kills sync_thread greenlet before joining it """
+        """Kills sync_thread greenlet before joining it"""
         # when stopping, `kill` will cause the `self.api.sync` call in _sync
         # to raise a connection error. This flag will ensure it exits gracefully then
         self.stop_event.set()
@@ -561,7 +560,7 @@ def set_presence_state(self, state: str) -> Dict:
         )
 
     def _mkroom(self, room_id: str) -> Room:
-        """ Uses a geventified Room subclass """
+        """Uses a geventified Room subclass"""
         if room_id not in self.rooms:
             self.rooms[room_id] = Room(self, room_id)
         room = self.rooms[room_id]
@@ -605,7 +604,7 @@ def blocking_sync(self, timeout_ms: int, latency_ms: int) -> None:
         self._handle_responses(pending_queue)
 
     def _sync(self, timeout_ms: int, latency_ms: int) -> None:
-        """ Reimplements MatrixClient._sync """
+        """Reimplements MatrixClient._sync"""
         log.debug(
             "Sync called",
             node=node_address_from_userid(self.user_id),
@@ -808,7 +807,6 @@ def _handle_responses(self, currently_queued_responses: List[JSONResponse]) -> N
 
                 room = self.rooms[room_id]
                 room.prev_batch = sync_room["timeline"]["prev_batch"]
-                room_members_count = len(room._members)
 
                 for event in sync_room["state"]["events"]:
                     event["room_id"] = room_id
@@ -817,9 +815,6 @@ def _handle_responses(self, currently_queued_responses: List[JSONResponse]) -> N
                     event["room_id"] = room_id
                     room._put_event(event)
 
-                # number of members changed. Verify validity of room
-                if room_members_count != len(room._members):
-                    self._handle_member_join_callback(room)
                 all_messages.append(
                     (
                         room,
@@ -851,7 +846,7 @@ def set_access_token(self, user_id: str, token: Optional[str]) -> None:
         self.token = self.api.token = token
 
     def set_sync_filter_id(self, sync_filter_id: Optional[int]) -> Optional[int]:
-        """ Sets the sync filter to the given id and returns previous filters id """
+        """Sets the sync filter to the given id and returns previous filters id"""
         prev_id = self._sync_filter_id
         self._sync_filter_id = sync_filter_id
         return prev_id
diff --git a/raiden/network/transport/matrix/rtc/web_rtc.py b/raiden/network/transport/matrix/rtc/web_rtc.py
index 14d1bf1b5b..816bd9d4b3 100644
--- a/raiden/network/transport/matrix/rtc/web_rtc.py
+++ b/raiden/network/transport/matrix/rtc/web_rtc.py
@@ -216,8 +216,8 @@ async def _try_signaling(self, coroutine: Coroutine) -> Optional[Any]:
             asyncio.create_task(self.close())
             return None
 
-        except AttributeError as ex:
-            self.log.error("Attribute error in coroutine", coroutine=coroutine, exception=ex)
+        except AttributeError:
+            self.log.exception("Attribute error in coroutine", coroutine=coroutine)
             asyncio.create_task(self.close())
             return None
 
diff --git a/raiden/network/transport/matrix/transport.py b/raiden/network/transport/matrix/transport.py
index 9b54969e08..d8fae55de7 100644
--- a/raiden/network/transport/matrix/transport.py
+++ b/raiden/network/transport/matrix/transport.py
@@ -11,18 +11,14 @@
 from uuid import uuid4
 
 import gevent
-import pkg_resources
 import structlog
 from aiortc import RTCSessionDescription
-from eth_utils import is_binary_address, to_normalized_address
+from eth_utils import encode_hex, is_binary_address, to_normalized_address
 from gevent.event import Event
-from gevent.lock import Semaphore
-from gevent.pool import Pool
 from gevent.queue import JoinableQueue
 from matrix_client.errors import MatrixError, MatrixHttpLibError
 from web3.types import BlockIdentifier
 
-import raiden
 from raiden.constants import (
     EMPTY_SIGNATURE,
     MATRIX_AUTO_SELECT_SERVER,
@@ -53,12 +49,11 @@
     MessageAckTimingKeeper,
     UserPresence,
     address_from_userid,
-    is_valid_userid,
-    join_broadcast_room,
+    capabilities_schema,
+    get_user_id_from_metadata,
     login,
     make_client,
     make_message_batches,
-    make_room_alias,
     make_user_id,
     my_place_or_yours,
     validate_and_parse_message,
@@ -74,8 +69,8 @@
 from raiden.utils.capabilities import capconfig_to_dict
 from raiden.utils.formatting import to_checksum_address
 from raiden.utils.logging import redact_secret
-from raiden.utils.notifying_queue import NotifyingQueue
 from raiden.utils.runnable import Runnable
+from raiden.utils.system import get_system_spec
 from raiden.utils.typing import (
     MYPY_ANNOTATION,
     Address,
@@ -91,12 +86,10 @@
     MessageID,
     NamedTuple,
     Optional,
-    PeerCapabilities,
     RoomID,
     Set,
     Tuple,
     UserID,
-    cast,
 )
 
 if TYPE_CHECKING:
@@ -120,11 +113,19 @@ class MessagesQueue:
     messages: List[Tuple[Message, Optional[AddressMetadata]]]
 
 
+def metadata_key_func(message_data: "_RetryQueue._MessageData") -> str:
+    address_metadata = message_data.address_metadata
+    if address_metadata is None:
+        return ""
+    uid = address_metadata.get("user_id", "")
+    return uid
+
+
 class _RetryQueue(Runnable):
-    """ A helper Runnable to send batched messages to receiver through transport """
+    """A helper Runnable to send batched messages to receiver through transport"""
 
     class _MessageData(NamedTuple):
-        """ Small helper data structure for message queue """
+        """Small helper data structure for message queue"""
 
         queue_identifier: QueueIdentifier
         message: Message
@@ -138,7 +139,6 @@ def __init__(self, transport: "MatrixTransport", receiver: Address) -> None:
         self.receiver = receiver
         self._message_queue: List[_RetryQueue._MessageData] = list()
         self._notify_event = gevent.event.Event()
-        self._lock = Semaphore()
         self._idle_since: int = 0  # Counter of idle iterations
         super().__init__()
         self.greenlet.name = f"RetryQueue recipient:{to_checksum_address(self.receiver)}"
@@ -170,53 +170,51 @@ def enqueue(
         queue_identifier: QueueIdentifier,
         messages: List[Tuple[Message, Optional[AddressMetadata]]],
     ) -> None:
-        """ Enqueue a message to be sent, and notify main loop """
+        """Enqueue a message to be sent, and notify main loop"""
         msg = (
             f"queue_identifier.recipient ({to_checksum_address(queue_identifier.recipient)}) "
             f" must match self.receiver ({to_checksum_address(self.receiver)})."
         )
         assert queue_identifier.recipient == self.receiver, msg
 
-        with self._lock:
-            timeout_generator = timeout_exponential_backoff(
-                self.transport._config.retries_before_backoff,
-                self.transport._config.retry_interval_initial,
-                self.transport._config.retry_interval_max,
+        timeout_generator = timeout_exponential_backoff(
+            self.transport._config.retries_before_backoff,
+            self.transport._config.retry_interval_initial,
+            self.transport._config.retry_interval_max,
+        )
+
+        encoded_messages = list()
+        for message, address_metadata in messages:
+            already_queued = any(
+                queue_identifier == data.queue_identifier and message == data.message
+                for data in self._message_queue
             )
 
-            encoded_messages = list()
-            for message, address_metadata in messages:
-                already_queued = any(
-                    queue_identifier == data.queue_identifier and message == data.message
-                    for data in self._message_queue
+            if already_queued:
+                self.log.warning(
+                    "Message already in queue - ignoring",
+                    receiver=to_checksum_address(self.receiver),
+                    queue=queue_identifier,
+                    message=redact_secret(DictSerializer.serialize(message)),
                 )
+            else:
+                expiration_generator = self._expiration_generator(timeout_generator)
+                data = _RetryQueue._MessageData(
+                    queue_identifier=queue_identifier,
+                    message=message,
+                    text=MessageSerializer.serialize(message),
+                    expiration_generator=expiration_generator,
+                    address_metadata=address_metadata,
+                )
+                encoded_messages.append(data)
 
-                if already_queued:
-                    self.log.warning(
-                        "Message already in queue - ignoring",
-                        receiver=to_checksum_address(self.receiver),
-                        queue=queue_identifier,
-                        message=redact_secret(DictSerializer.serialize(message)),
-                    )
-                else:
-                    expiration_generator = self._expiration_generator(timeout_generator)
-                    data = _RetryQueue._MessageData(
-                        queue_identifier=queue_identifier,
-                        message=message,
-                        text=MessageSerializer.serialize(message),
-                        expiration_generator=expiration_generator,
-                        address_metadata=address_metadata,
-                    )
-                    encoded_messages.append(data)
-
-            self._message_queue.extend(encoded_messages)
-
+        self._message_queue.extend(encoded_messages)
         self.notify()
 
     def enqueue_unordered(
         self, message: Message, address_metadata: AddressMetadata = None
     ) -> None:
-        """ Helper to enqueue a message in the unordered queue. """
+        """Helper to enqueue a message in the unordered queue."""
         self.enqueue(
             queue_identifier=QueueIdentifier(
                 recipient=self.receiver, canonical_identifier=CANONICAL_IDENTIFIER_UNORDERED_QUEUE
@@ -225,32 +223,8 @@ def enqueue_unordered(
         )
 
     def notify(self) -> None:
-        """ Notify main loop to check if anything needs to be sent """
-        with self._lock:
-            self._notify_event.set()
-
-    def _batch_by_address_metadata(
-        self,
-    ) -> List[Tuple[List[_MessageData], Optional[AddressMetadata]]]:
-        def key_func(message_data: "_RetryQueue._MessageData") -> str:
-            address_metadata = message_data.address_metadata
-            if address_metadata is None:
-                return ""
-            uid = address_metadata.get("user_id", "")
-            return cast(str, uid)
-
-        batched_messages = list()
-        queue_by_user_id = sorted(self._message_queue[:], key=key_func)
-        for user_id, batch in itertools.groupby(queue_by_user_id, key_func):
-            message_data_batch = list(batch)
-            if user_id == "":
-                metadata = None
-            else:
-                # simply use the first metadata in the list, event though
-                # there could be discrepancies along the batch
-                metadata = message_data_batch[0].address_metadata
-            batched_messages.append((message_data_batch, metadata))
-        return batched_messages
+        """Notify main loop to check if anything needs to be sent"""
+        self._notify_event.set()
 
     def _check_and_send(self) -> None:
         """Check and send all pending/queued messages that are not waiting on retry timeout
@@ -265,8 +239,6 @@ def _check_and_send(self) -> None:
             self.log.warning("Can't retry", reason="Transport stopped")
             return
 
-        assert self._lock.locked(), "RetryQueue lock must be held while messages are being sent"
-
         # On startup protocol messages must be sent only after the monitoring
         # services are updated. For more details refer to the method
         # `RaidenService._initialize_monitoring_services_queue`
@@ -290,9 +262,19 @@ def message_is_in_queue(message_data: _RetryQueue._MessageData) -> bool:
                 for send_event in self.transport._queueids_to_queues[message_data.queue_identifier]
             )
 
-        message_texts: List[str] = list()
-        for subqueue, address_metadata in self._batch_by_address_metadata():
-            for message_data in subqueue:
+        # batch by user_id, so that we can potentially combine data for send-to-device calls
+        queue_by_user_id = sorted(self._message_queue[:], key=metadata_key_func)
+        for user_id, batch in itertools.groupby(queue_by_user_id, metadata_key_func):
+            message_data_batch = list(batch)
+            if user_id == "":
+                address_metadata = None
+            else:
+                # simply use the first metadata in the list, event though
+                # there could be discrepancies along the batch
+                address_metadata = message_data_batch[0].address_metadata
+
+            message_texts: List[str] = list()
+            for message_data in message_data_batch:
                 # Messages are sent on two conditions:
                 # - Non-retryable (e.g. Delivered)
                 #   - Those are immediately remove from the local queue
@@ -329,7 +311,6 @@ def message_is_in_queue(message_data: _RetryQueue._MessageData) -> bool:
                                         message_data.message.message_identifier,
                                     )
                                 ] += 1
-
                 if remove:
                     self._message_queue.remove(message_data)
 
@@ -356,14 +337,12 @@ def _run(self) -> None:  # type: ignore
         )
         # run while transport parent is running
         while not self.transport._stop_event.ready():
-            # once entered the critical section, block any other enqueue or notify attempt
-            with self._lock:
-                self._notify_event.clear()
-                if self._message_queue:
-                    self._idle_since = 0
-                    self._check_and_send()
-                else:
-                    self._idle_since += 1
+            self._notify_event.clear()
+            if self._message_queue:
+                self._idle_since = 0
+                self._check_and_send()
+            else:
+                self._idle_since += 1
 
             if self.is_idle:
                 # There have been no messages to process for a while. Exit.
@@ -403,7 +382,6 @@ class ReceivedCallMessage(_ReceivedMessageBase):
 class MatrixTransport(Runnable):
     _room_prefix = "raiden"
     _room_sep = "_"
-    _healthcheck_queue: NotifyingQueue[Address]
     log = log
 
     def __init__(self, config: MatrixTransportConfig, environment: Environment) -> None:
@@ -431,10 +409,9 @@ def _http_retry_delay() -> Iterable[float]:
                 self._config.retry_interval_max,
             )
 
-        version = pkg_resources.require(raiden.__name__)[0].version
+        version = get_system_spec()["raiden"]
         self._client: GMatrixClient = make_client(
             self._handle_sync_messages,
-            self._handle_member_join,
             homeserver_candidates,
             http_pool_maxsize=4,
             http_retry_timeout=40,
@@ -457,6 +434,7 @@ def _http_retry_delay() -> Iterable[float]:
         # FIXME this will not update the servers while Raiden is running,
         #  thus to-device user-id fallback will only try to send to those home-servers that
         #  are fetched during config time
+        #  this fixme is about servers joining the federation during runtime of a raiden node
         self._all_server_names = {self._server_name}
         for server_url in config.available_servers:
             self._all_server_names.add(urlparse(server_url).netloc)
@@ -469,7 +447,6 @@ def _http_retry_delay() -> Iterable[float]:
         self._address_to_retrier: Dict[Address, _RetryQueue] = dict()
         self._displayname_cache = DisplayNameCache()
 
-        self.broadcast_rooms: Dict[str, Room] = dict()
         self._broadcast_queue: JoinableQueue[Tuple[str, Message]] = JoinableQueue()
 
         self._started = False
@@ -477,7 +454,6 @@ def _http_retry_delay() -> Iterable[float]:
 
         self._stop_event: Event = Event()
         self._stop_event.set()
-        self._healthcheck_queue = NotifyingQueue()
 
         self._broadcast_event = Event()
         self._prioritize_broadcast_messages: bool = True
@@ -522,19 +498,24 @@ def user_id(self) -> Optional[UserID]:
         address = self._node_address
         return make_user_id(address, self._server_name) if address is not None else None
 
+    @property
+    def displayname(self) -> str:
+        if self._raiden_service is None:
+            return ""
+        signature_bytes = self._raiden_service.signer.sign(str(self.user_id).encode())
+        return encode_hex(signature_bytes)
+
     @property
     def address_metadata(self) -> Optional[AddressMetadata]:
-        own_caps = PeerCapabilities(capconfig_to_dict(self._config.capabilities_config))
+        cap_dict = capconfig_to_dict(self._config.capabilities_config)
+        own_caps = capabilities_schema.dump({"capabilities": cap_dict})["capabilities"]
         own_user_id = self.user_id
         if own_user_id is None:
             return None
-        return dict(user_id=own_user_id, capabilities=own_caps)
+        return dict(user_id=own_user_id, capabilities=own_caps, displayname=self.displayname)
 
     def start(  # type: ignore
-        self,
-        raiden_service: "RaidenService",
-        prev_auth_data: Optional[str],
-        health_check_list: Optional[List[Address]] = None,  # pylint: disable=unused-argument
+        self, raiden_service: "RaidenService", prev_auth_data: Optional[str]
     ) -> None:
         if not self._stop_event.ready():
             raise RuntimeError(f"{self!r} already started")
@@ -544,8 +525,6 @@ def start(  # type: ignore
         self._raiden_service = raiden_service
         self._web_rtc_manager.node_address = self._raiden_service.address
 
-        # XXX-UAM health_check_list was used to whitelist/healthcheck
-        #  addresses after replaying state
         assert asyncio.get_event_loop().is_running(), "the loop must be running"
         self.log.debug("Asyncio loop is running", running=asyncio.get_event_loop().is_running())
 
@@ -574,7 +553,6 @@ def start(  # type: ignore
             node=to_checksum_address(self._raiden_service.address),
             transport_uuid=str(self._uuid),
         )
-        self._initialize_broadcast_rooms()
         self._initialize_first_sync()
         self._initialize_sync()
 
@@ -603,7 +581,7 @@ def _set_presence(self, state: UserPresence) -> None:
         self._client.set_presence_state(state.value)
 
     def _run(self) -> None:  # type: ignore
-        """ Runnable main method, perform wait on long-running subtasks """
+        """Runnable main method, perform wait on long-running subtasks"""
         # dispatch auth data on first scheduling after start
         assert self._raiden_service is not None, "_raiden_service not set"
         self.greenlet.name = f"MatrixTransport._run node:{self.checksummed_address}"
@@ -701,14 +679,6 @@ def stop(self) -> None:
         # parent may want to call get() after stop(), to ensure _run errors are re-raised
         # we don't call it here to avoid deadlock when self crashes and calls stop() on finally
 
-    def async_start_health_check(self, node_address: Address) -> None:
-        # XXX-UAM implicitly used for whitelisting addresses
-        pass
-
-    def immediate_health_check_for(self, node_address: Address) -> None:
-        # XXX-UAM implicitly used for whitelisting addresses
-        pass
-
     def send_async(self, message_queues: List[MessagesQueue]) -> None:
         """Queue messages to be sent.
 
@@ -856,9 +826,7 @@ def _initialize_first_sync(self) -> None:
 
         # Call sync to fetch the inventory rooms and new invites, the sync
         # limit prevents fetching the messages.
-        filter_id = self._client.create_sync_filter(
-            not_rooms=self.broadcast_rooms.values(), limit=0
-        )
+        filter_id = self._client.create_sync_filter(limit=0)
         prev_sync_filter_id = self._client.set_sync_filter_id(filter_id)
         # Need to reset this here, otherwise we might run into problems after a restart
         self._client.last_sync = float("inf")
@@ -873,29 +841,6 @@ def _initialize_first_sync(self) -> None:
             # Leave any that are still lingering.
             room.leave()
 
-    def _initialize_broadcast_rooms(self) -> None:
-        msg = "To join the broadcast rooms the Matrix client to be properly authenticated."
-        assert self._user_id, msg
-
-        pool = Pool(size=10)
-
-        def _join_broadcast_room(transport: MatrixTransport, room_name: str) -> None:
-            broadcast_room_alias = f"#{room_name}:{transport._server_name}"
-            transport.log.debug(
-                "Joining broadcast room", broadcast_room_alias=broadcast_room_alias
-            )
-            transport.broadcast_rooms[room_name] = join_broadcast_room(
-                client=transport._client, broadcast_room_alias=broadcast_room_alias
-            )
-
-        for suffix in self._config.broadcast_rooms:
-            alias_prefix = make_room_alias(self.chain_id, suffix)
-
-            if alias_prefix not in self.broadcast_rooms:
-                pool.apply_async(_join_broadcast_room, args=(self, alias_prefix))
-
-        pool.join(raise_error=True)
-
     def _initialize_sync(self) -> None:
         msg = "_initialize_sync requires the GMatrixClient to be properly authenticated."
         assert self._user_id, msg
@@ -904,18 +849,6 @@ def _initialize_sync(self) -> None:
         assert self._client.sync_worker is None, msg
         assert self._client.message_worker is None, msg
 
-        msg = (
-            "The node must have joined the broadcast rooms before starting the "
-            "sync thread, since that is necessary to properly generate the "
-            "filters."
-        )
-        assert self.broadcast_rooms, msg
-
-        broadcast_filter_id = self._client.create_sync_filter(
-            not_rooms=self.broadcast_rooms.values()
-        )
-        self._client.set_sync_filter_id(broadcast_filter_id)
-
         def on_success(greenlet: gevent.Greenlet) -> None:
             if greenlet in self.greenlets:
                 self.greenlets.remove(greenlet)
@@ -989,14 +922,6 @@ def _reject_invite(self, room_id: RoomID, state: dict) -> None:
             content={"membership": "leave"},
         )
 
-    def _handle_member_join(self, room: Room) -> None:
-        if self._is_broadcast_room(room):
-            raise AssertionError(
-                f"Broadcast room events should be filtered in syncs: {room.canonical_alias}."
-                f"Joined Broadcast Rooms: {list(self.broadcast_rooms.keys())}"
-                f"Should be joined to: {self._config.broadcast_rooms}"
-            )
-
     def _validate_matrix_messages(
         self, room: Optional[Room], messages: List[MatrixMessage]
     ) -> Tuple[List[ReceivedRaidenMessage], List[ReceivedCallMessage]]:
@@ -1038,12 +963,7 @@ def _validate_matrix_messages(
                 )
                 continue
 
-            if room and self._is_broadcast_room(room):
-                # This must not happen. Nodes must not listen on broadcast rooms.
-                raise RaidenUnrecoverableError(
-                    f"Received message in broadcast room {room.canonical_alias}"
-                )
-            elif room:
+            if room:
                 # We no longer accept node-to-node communication messages in private rooms and
                 # should not be in any.
                 raise RaidenUnrecoverableError(f"Received message in room {room.canonical_alias}.")
@@ -1100,7 +1020,7 @@ def _process_raiden_messages(self, all_messages: List[ReceivedRaidenMessage]) ->
         self._raiden_service.on_messages(incoming_messages)
 
     def _handle_sync_messages(self, sync_messages: MatrixSyncMessages) -> bool:
-        """ Handle text messages sent to listening rooms """
+        """Handle text messages sent to listening rooms"""
         if self._stop_event.ready():
             return False
 
@@ -1179,7 +1099,7 @@ def _process_call_messages(self, call_messages: List[ReceivedCallMessage]) -> No
                 continue
 
     def _get_retrier(self, receiver: Address) -> _RetryQueue:
-        """ Construct and return a _RetryQueue for receiver """
+        """Construct and return a _RetryQueue for receiver"""
         retrier = self._address_to_retrier.get(receiver)
         # The RetryQueue may have exited due to being idle
         if retrier is None or retrier.greenlet.ready():
@@ -1230,29 +1150,6 @@ def _send_to_device_raw(
 
         self._client.api.send_to_device(event_type="m.room.message", messages=messages)
 
-    def _get_possible_user_ids(
-        self, address: Address, address_metadata: AddressMetadata = None
-    ) -> Set[UserID]:
-        """Construct possible user-ids from the address
-
-        This will take the information from the connected matrix homeservers and
-        an optional AddressMetadata dictionary.
-        If the address metadata doesn't further specify what user-id to use for that
-        address, then this method will simply construct all possible user-ids on all
-        known matrix homeservers.
-        """
-        user_ids = set()
-        if address_metadata is not None:
-            user_id = address_metadata.get("user_id")
-            if is_valid_userid(user_id):
-                user_id = cast(UserID, user_id)
-                user_ids.add(user_id)
-        if not user_ids:
-            user_ids = {
-                make_user_id(address, server_name) for server_name in self._all_server_names
-            }
-        return user_ids
-
     def _send_raw(
         self,
         receiver_address: Address,
@@ -1266,11 +1163,21 @@ def _send_raw(
         if self._web_rtc_manager.has_ready_channel(receiver_address):
             communication_medium = CommunicationMedium.WEB_RTC
         else:
-            user_ids = self._get_possible_user_ids(receiver_address, receiver_metadata)
+            user_id = get_user_id_from_metadata(receiver_address, receiver_metadata)
             # Don't check whether the to-device capability is set -
             # this will only happen for older, incompatible clients that
             # will simply ignore our communication attempt
             communication_medium = CommunicationMedium.TO_DEVICE
+            if user_id is None:
+                self.log.debug(
+                    "Cannot send raw message without address metadata.",
+                    receiver=to_checksum_address(receiver_address),
+                    send_medium=communication_medium.value,
+                    data=data.replace("\n", "\\n"),
+                )
+                return
+            else:
+                user_ids.add(user_id)
 
         self.log.debug(
             "Send raw message",
@@ -1435,16 +1342,10 @@ def _handle_closed_connection(self, partner_address: Address) -> None:
         gevent.sleep(3)
         self._maybe_initialize_web_rtc(partner_address)
 
-    def _is_broadcast_room(self, room: Room) -> bool:
-        has_alias = room.canonical_alias is not None
-        return has_alias and any(
-            suffix in room.canonical_alias for suffix in self._config.broadcast_rooms
-        )
-
     def _capability_usable(
         self, capability: Capabilities, partner_capabilities_config: CapabilitiesConfig
     ) -> bool:
-        """ Checks if a given capability is enabled for the local and the partner node """
+        """Checks if a given capability is enabled for the local and the partner node"""
 
         own_caps = capconfig_to_dict(self._config.capabilities_config)
         partner_caps = capconfig_to_dict(partner_capabilities_config)
@@ -1479,7 +1380,7 @@ def _address_reachability_changed(
         assert self._raiden_service is not None, "_raiden_service not set"
 
     def _sign(self, data: bytes) -> bytes:
-        """ Use eth_sign compatible hasher to sign matrix data """
+        """Use eth_sign compatible hasher to sign matrix data"""
         assert self._raiden_service is not None, "_raiden_service not set"
         return self._raiden_service.signer.sign(data=data)
 
diff --git a/raiden/network/transport/matrix/utils.py b/raiden/network/transport/matrix/utils.py
index 5e3372d7fd..1fdf1e8fe2 100644
--- a/raiden/network/transport/matrix/utils.py
+++ b/raiden/network/transport/matrix/utils.py
@@ -6,7 +6,7 @@
 from datetime import datetime
 from enum import Enum
 from functools import lru_cache
-from operator import attrgetter, itemgetter
+from operator import itemgetter
 from typing import Any, Callable, Dict, Generator, Iterable, List, Optional, Sequence, Set
 from urllib.parse import urlparse
 
@@ -25,18 +25,12 @@
 
 from raiden.api.v1.encoding import CapabilitiesSchema
 from raiden.constants import DeviceIDs
-from raiden.exceptions import (
-    InvalidSignature,
-    RaidenUnrecoverableError,
-    SerializationError,
-    TransportError,
-)
+from raiden.exceptions import InvalidSignature, SerializationError, TransportError
 from raiden.messages.abstract import Message, RetrieableMessage, SignedMessage
 from raiden.messages.synchronization import Processed
 from raiden.network.transport.matrix.client import (
     GMatrixClient,
     MatrixSyncMessages,
-    Room,
     User,
     node_address_from_userid,
 )
@@ -44,7 +38,17 @@
 from raiden.storage.serialization.serializer import MessageSerializer
 from raiden.utils.gevent import spawn_named
 from raiden.utils.signer import Signer, recover
-from raiden.utils.typing import Address, ChainID, MessageID, Signature, T_UserID, UserID, typecheck
+from raiden.utils.typing import (
+    Address,
+    AddressMetadata,
+    ChainID,
+    MessageID,
+    Signature,
+    T_UserID,
+    UserID,
+    cast,
+    typecheck,
+)
 from raiden_contracts.constants import ID_TO_CHAINNAME
 
 log = structlog.get_logger(__name__)
@@ -107,12 +111,32 @@ def address_from_userid(user_id: str) -> Optional[Address]:
     return address
 
 
-def is_valid_userid(user_id: Any) -> bool:
+def is_valid_userid_for_address(user_id: Any, address: Address) -> bool:
     try:
         typecheck(user_id, T_UserID)
     except ValueError:
         return False
-    return bool(USERID_RE.match(user_id))
+    user_id_address = address_from_userid(user_id)
+    if not user_id_address:
+        return False
+    return address == user_id_address
+
+
+def get_user_id_from_metadata(
+    address: Address, address_metadata: AddressMetadata = None
+) -> Optional[UserID]:
+    """Get user-id from the address-metadata, if it is valid and present.
+
+    This will take the information from an optional AddressMetadata dictionary.
+    If the address_metadata is present and the user-id within that metadata is
+    valid and present for the specified address, the user-id will get returned.
+    """
+    if address_metadata is not None:
+        user_id = address_metadata.get("user_id")
+        if is_valid_userid_for_address(user_id, address):
+            user_id = cast(UserID, user_id)
+            return user_id
+    return None
 
 
 class DisplayNameCache:
@@ -184,29 +208,6 @@ def generate_report(self) -> List[float]:
         return sorted(self._durations)
 
 
-def join_broadcast_room(client: GMatrixClient, broadcast_room_alias: str) -> Room:
-    """Join the public broadcast through the alias `broadcast_room_alias`.
-
-    When a new Matrix instance is deployed the broadcast room _must_ be created
-    and aliased, Raiden will not use a server that does not have the discovery
-    room properly set. Requiring the setup of the broadcast alias as part of
-    the server setup fixes a serious race condition where multiple discovery
-    rooms are created, which would break the presence checking.
-    See: https://github.com/raiden-network/raiden-transport/issues/46
-    """
-    try:
-        room = client.join_room(broadcast_room_alias)
-        del client.rooms[room.room_id]
-        return room
-    except MatrixRequestError:
-        raise RaidenUnrecoverableError(
-            f"Could not join broadcast room {broadcast_room_alias}. "
-            f"Make sure the Matrix server you're trying to connect to uses the recommended server "
-            f"setup, esp. the server-side broadcast room creation. "
-            f"See https://github.com/raiden-network/raiden-transport."
-        )
-
-
 def first_login(
     client: GMatrixClient,
     signer: Signer,
@@ -301,8 +302,7 @@ def first_login(
         current_capabilities = ""
 
     # Only set the capabilities if necessary.
-    cap_str = capabilities_schema.load(capabilities.get("capabilities", {}))
-    cap_str = cap_str["capabilities"]
+    cap_str = capabilities.get("capabilities", "mxc://")
     if current_capabilities != cap_str:
         user.set_avatar_url(cap_str)
 
@@ -311,6 +311,7 @@ def first_login(
         node=to_checksum_address(username),
         homeserver=server_name,
         server_url=server_url,
+        capabilities=capabilities,
     )
     return user
 
@@ -388,25 +389,27 @@ def login(
             )
 
     try:
-        cap: Dict = capabilities_schema.dump(capabilities)
+        cap: Dict = capabilities_schema.dump({"capabilities": capabilities})
     except ValueError:
         raise Exception("error serializing")
     return first_login(client, signer, username, cap, device_id)
 
 
-@cached(cache=LRUCache(128), key=attrgetter("user_id", "displayname"), lock=Semaphore())  # type: ignore # noqa E501
 def validate_userid_signature(user: User) -> Optional[Address]:
-    """ Validate a userId format and signature on displayName, and return its address"""
+    """Validate a userId format and signature on displayName, and return its address"""
+    return validate_user_id_signature(user.user_id, user.displayname)
+
+
+@cached(cache=LRUCache(128), lock=Semaphore())  # noqa E501
+def validate_user_id_signature(user_id: UserID, displayname: Optional[str]) -> Optional[Address]:
     # display_name should be an address in the USERID_RE format
-    match = USERID_RE.match(user.user_id)
+    match = USERID_RE.match(user_id)
     if not match:
-        log.warning("Invalid user id", user=user.user_id)
+        log.warning("Invalid user id", user=user_id)
         return None
 
-    displayname = user.displayname
-
     if displayname is None:
-        log.warning("Displayname not set", user=user.user_id)
+        log.warning("Displayname not set", user=user_id)
         return None
 
     encoded_address = match.group(1)
@@ -416,11 +419,11 @@ def validate_userid_signature(user: User) -> Optional[Address]:
         if DISPLAY_NAME_HEX_RE.match(displayname):
             signature_bytes = decode_hex(displayname)
         else:
-            log.warning("Displayname invalid format", user=user.user_id, displayname=displayname)
+            log.warning("Displayname invalid format", user=user_id, displayname=displayname)
             return None
-        recovered = recover(data=user.user_id.encode(), signature=Signature(signature_bytes))
+        recovered = recover(data=user_id.encode(), signature=Signature(signature_bytes))
         if not (address and recovered and recovered == address):
-            log.warning("Unexpected signer of displayname", user=user.user_id)
+            log.warning("Unexpected signer of displayname", user=user_id)
             return None
     except (
         DecodeError,
@@ -489,7 +492,6 @@ def sort_servers_closest(
 
 def make_client(
     handle_messages_callback: Callable[[MatrixSyncMessages], bool],
-    handle_member_join_callback: Callable[[Room], None],
     servers: List[str],
     *args: Any,
     **kwargs: Any,
@@ -514,7 +516,6 @@ def make_client(
     for server_url, rtt in sorted_servers.items():
         client = GMatrixClient(
             handle_messages_callback,
-            handle_member_join_callback,
             server_url,
             *args,
             **kwargs,
@@ -625,7 +626,7 @@ def my_place_or_yours(our_address: Address, partner_address: Address) -> Address
 def make_message_batches(
     message_texts: Iterable[str], _max_batch_size: int = MATRIX_MAX_BATCH_SIZE
 ) -> Generator[str, None, None]:
-    """ Group messages into newline separated batches not exceeding ``_max_batch_size``. """
+    """Group messages into newline separated batches not exceeding ``_max_batch_size``."""
     current_batch: List[str] = []
     size = 0
     for message_text in message_texts:
diff --git a/raiden/raiden_event_handler.py b/raiden/raiden_event_handler.py
index e1fbe56985..fece2015f2 100644
--- a/raiden/raiden_event_handler.py
+++ b/raiden/raiden_event_handler.py
@@ -63,7 +63,6 @@
     EventUnlockSuccess,
     SendLockedTransfer,
     SendLockExpired,
-    SendRefundTransfer,
     SendSecretRequest,
     SendSecretReveal,
     SendUnlock,
@@ -177,7 +176,6 @@ def on_raiden_events(
             SendSecretReveal: [self.handle_send_secretreveal, [message_queues]],
             SendUnlock: [self.handle_send_balanceproof, [message_queues]],
             SendSecretRequest: [self.handle_send_secretrequest, [chain_state, message_queues]],
-            SendRefundTransfer: [self.handle_send_refundtransfer, [message_queues]],
             SendWithdrawRequest: [self.handle_send_withdrawrequest, [message_queues]],
             SendWithdrawConfirmation: [self.handle_send_withdraw, [message_queues]],
             SendWithdrawExpired: [self.handle_send_withdrawexpired, [message_queues]],
@@ -282,18 +280,6 @@ def handle_send_secretrequest(
             (secret_request_message, secret_request_event.recipient_metadata)
         )
 
-    @staticmethod
-    def handle_send_refundtransfer(
-        raiden: "RaidenService",
-        refund_transfer_event: SendRefundTransfer,
-        message_queues: Dict[QueueIdentifier, List[Tuple[Message, Optional[AddressMetadata]]]],
-    ) -> None:  # pragma: no unittest
-        refund_transfer_message = message_from_sendevent(refund_transfer_event)
-        raiden.sign(refund_transfer_message)
-        message_queues[refund_transfer_event.queue_identifier].append(
-            (refund_transfer_message, refund_transfer_event.recipient_metadata)
-        )
-
     @staticmethod
     def handle_send_withdrawrequest(
         raiden: "RaidenService",
@@ -836,7 +822,7 @@ def handle_update_services_addresses(
 
 
 class PFSFeedbackEventHandler(RaidenEventHandler):
-    """ A event handler that sends feedback to the PFS. """
+    """A event handler that sends feedback to the PFS."""
 
     def __init__(self, wrapped_handler: EventHandler) -> None:
         self.wrapped = wrapped_handler
diff --git a/raiden/raiden_service.py b/raiden/raiden_service.py
index aed9bce834..262e825aa0 100644
--- a/raiden/raiden_service.py
+++ b/raiden/raiden_service.py
@@ -23,7 +23,6 @@
 from raiden.blockchain.decode import blockchainevent_to_statechange
 from raiden.blockchain.events import BlockchainEvents
 from raiden.blockchain.filters import RaidenContractFilter
-from raiden.blockchain_events_handler import after_blockchain_statechange
 from raiden.constants import (
     ABSENT_SECRET,
     BLOCK_ID_LATEST,
@@ -299,7 +298,7 @@ class SynchronizationState(Enum):
 
 
 class RaidenService(Runnable):
-    """ A Raiden node. """
+    """A Raiden node."""
 
     def __init__(
         self,
@@ -450,7 +449,7 @@ def __init__(
         self.state_change_qty = 0
 
     def start(self) -> None:
-        """ Start the node synchronously. Raises directly if anything went wrong on startup """
+        """Start the node synchronously. Raises directly if anything went wrong on startup"""
         assert self.stop_event.ready(), f"Node already started. node:{self!r}"
         self.stop_event.clear()
         self.greenlets = list()
@@ -478,7 +477,7 @@ def start(self) -> None:
         self.transport.greenlet.link_exception(self.on_error)
         if self.api_server:
             self.api_server.greenlet.link_exception(self.on_error)
-        self._start_transport(chain_state)
+        self._start_transport()
         self._start_alarm_task()
 
         log.debug("Raiden Service started", node=to_checksum_address(self.address))
@@ -487,7 +486,7 @@ def start(self) -> None:
         self._set_rest_api_service_available()
 
     def _run(self, *args: Any, **kwargs: Any) -> None:  # pylint: disable=method-hidden
-        """ Busy-wait on long-lived subtasks/greenlets, re-raise if any error occurs """
+        """Busy-wait on long-lived subtasks/greenlets, re-raise if any error occurs"""
         self.greenlet.name = f"RaidenService._run node:{to_checksum_address(self.address)}"
         try:
             self.stop_event.wait()
@@ -500,7 +499,7 @@ def _run(self, *args: Any, **kwargs: Any) -> None:  # pylint: disable=method-hid
             raise
 
     def stop(self) -> None:
-        """ Stop the node gracefully. Raise if any stop-time error occurred on any subtask """
+        """Stop the node gracefully. Raise if any stop-time error occurred on any subtask"""
         if self.stop_event.ready():  # not started
             return
 
@@ -564,7 +563,7 @@ def privkey(self) -> PrivateKey:
         return self.rpc_client.privkey
 
     def add_pending_greenlet(self, greenlet: Greenlet) -> None:
-        """ Ensures an error on the passed greenlet crashes self/main greenlet. """
+        """Ensures an error on the passed greenlet crashes self/main greenlet."""
 
         def remove(_: Any) -> None:
             self.greenlets.remove(greenlet)
@@ -576,7 +575,7 @@ def remove(_: Any) -> None:
     def __repr__(self) -> str:
         return f"<{self.__class__.__name__} node:{to_checksum_address(self.address)}>"
 
-    def _start_transport(self, chain_state: ChainState) -> None:
+    def _start_transport(self) -> None:
         """Initialize the transport and related facilities.
 
         Note:
@@ -592,23 +591,12 @@ def _start_transport(self, chain_state: ChainState) -> None:
         )
         assert self.blockchain_events is not None, msg
 
-        # XXX-UAM health-check list was passed to transport after a recover,
-        #  to start tracking / whitlisting e.g. initiator / target nodes
-        # health_check_list = self._get_initial_health_check_list(chain_state)
-        # log.debug(
-        #     "Initializing health checks",
-        #     neighbour_nodes=[to_checksum_address(address) for address in health_check_list],
-        #     node=to_checksum_address(self.address),
-        # )
         if self.default_service_registry is not None:
             populate_services_addresses(
                 self.transport, self.default_service_registry, BLOCK_ID_LATEST
             )
         self.transport.start(raiden_service=self, prev_auth_data=None)
 
-        for neighbour in views.all_neighbour_nodes(chain_state):
-            self.async_start_health_check_for(neighbour)
-
     def _make_initial_state(self) -> ChainState:
         # On first run Raiden needs to fetch all events for the payment
         # network, to reconstruct all token network graphs and find opened
@@ -967,9 +955,6 @@ def _trigger_state_change_effects(
                 raiden=self, canonical_identifier=canonical_identifier, update_fee_schedule=True
             )
 
-        for state_change in state_changes:
-            after_blockchain_statechange(self, state_change)
-
         log.debug(
             "Raiden events",
             node=to_checksum_address(self.address),
@@ -1069,28 +1054,6 @@ def _handle_events(self, chain_state: ChainState, raiden_events: List[RaidenEven
             else:
                 raise
 
-    def async_start_health_check_for(self, node_address: Address) -> None:
-        """Start health checking `node_address`.
-
-        This function is a noop during initialization, because health checking
-        can be started as a side effect of some events (e.g. new channel). For
-        these cases the healthcheck will be started by
-        `_start_transport`.
-        """
-        if self.transport:
-            self.transport.async_start_health_check(node_address)
-
-    def immediate_health_check_for(self, node_address: Address) -> None:
-        """Start health checking `node_address`.
-
-        This function is a noop during initialization, because health checking
-        can be started as a side effect of some events (e.g. new channel). For
-        these cases the healthcheck will be started by
-        `_start_transport`.
-        """
-        if self.transport:
-            self.transport.immediate_health_check_for(node_address)
-
     def _best_effort_synchronize(self, latest_block: BlockData) -> SynchronizationState:
         """Called with the current latest block, tries to synchronize with the
         *confirmed* head of the chain in a best effort manner, it is not
@@ -1372,7 +1335,14 @@ def _initialize_messages_queues(self, chain_state: ChainState) -> None:
             for event in event_queue:
                 message = message_from_sendevent(event)
                 self.sign(message)
-                queue_messages.append((message, None))
+                # FIXME: this will load the recipient's metadata from the persisted
+                #  state. If the recipient roamed during the offline time of our node,
+                #  the message will never reach the recipient,
+                #  especially since we don't have a WebRTC connection with the recipient at
+                #  startup.
+                #  Depending on the time our node is offline, roaming of the recipient
+                #  can become more likely.
+                queue_messages.append((message, event.recipient_metadata))
 
             all_messages.append(MessagesQueue(queue_identifier, queue_messages))
 
@@ -1486,28 +1456,8 @@ def _initialize_channel_fees(self) -> None:
                     update_fee_schedule=True,
                 )
 
-    def _get_initial_health_check_list(self, chain_state: ChainState) -> List[Address]:
-        """ Fetch direct neighbors and mediated transfer targets on transport """
-        neighbour_addresses: List[Address] = []
-
-        all_neighbour_nodes = views.all_neighbour_nodes(chain_state)
-
-        for neighbour in all_neighbour_nodes:
-            neighbour_addresses.append(neighbour)
-
-        events_queues = views.get_all_messagequeues(chain_state)
-
-        for event_queue in events_queues.values():
-            for event in event_queue:
-                if isinstance(event, SendLockedTransfer):
-                    transfer = event.transfer
-                    if transfer.initiator == InitiatorAddress(self.address):
-                        neighbour_addresses.append(Address(transfer.target))
-
-        return neighbour_addresses
-
     def sign(self, message: Message) -> None:
-        """ Sign message inplace. """
+        """Sign message inplace."""
         if not isinstance(message, SignedMessage):
             raise ValueError("{} is not signable.".format(repr(message)))
 
@@ -1575,8 +1525,6 @@ def mediated_transfer_async(
                 f" That secret is already registered onchain."
             )
 
-        self.async_start_health_check_for(Address(target))
-
         # Checks if there is a payment in flight with the same payment_id and
         # target. If there is such a payment and the details match, instead of
         # starting a new payment this will give the caller the existing
@@ -1630,10 +1578,16 @@ def mediated_transfer_async(
         return payment_status
 
     def withdraw(
-        self, canonical_identifier: CanonicalIdentifier, total_withdraw: WithdrawAmount
+        self,
+        canonical_identifier: CanonicalIdentifier,
+        total_withdraw: WithdrawAmount,
+        recipient_metadata: AddressMetadata = None,
     ) -> None:
+
         init_withdraw = ActionChannelWithdraw(
-            canonical_identifier=canonical_identifier, total_withdraw=total_withdraw
+            canonical_identifier=canonical_identifier,
+            total_withdraw=total_withdraw,
+            recipient_metadata=recipient_metadata,
         )
 
         self.handle_and_track_state_changes([init_withdraw])
diff --git a/raiden/routing.py b/raiden/routing.py
index d9db7da0df..7debd8c573 100644
--- a/raiden/routing.py
+++ b/raiden/routing.py
@@ -72,20 +72,24 @@ def get_best_routes(
                     if address_metadata:
                         address_to_address_metadata[Address(to_address)] = address_metadata
                 except ServiceRequestFailed as ex:
-                    log.warning(
-                        f"PFS returned an error while trying to fetch user information: \n{ex}"
-                    )
+                    msg = f"PFS returned an error while trying to fetch user information: \n{ex}"
+                    log.warning(msg)
+                    return msg, [], None
 
-                direct_route = RouteState(
-                    route=[Address(from_address), Address(to_address)],
-                    estimated_fee=FeeAmount(0),
-                    address_to_metadata=address_to_address_metadata,
-                )
-                return None, [direct_route], None
+                try:
+                    direct_route = RouteState(
+                        route=[Address(from_address), Address(to_address)],
+                        estimated_fee=FeeAmount(0),
+                        address_to_metadata=address_to_address_metadata,
+                    )
+                    return None, [direct_route], None
+                except ValueError as ex:
+                    return str(ex), [], None
 
     if pfs_config is None or one_to_n_address is None:
-        log.warning("Pathfinding Service could not be used.")
-        return "Pathfinding Service could not be used.", list(), None
+        msg = "Pathfinding Service could not be used."
+        log.warning(msg)
+        return msg, list(), None
 
     # Does any channel have sufficient capacity for the payment?
     channels = [
@@ -239,8 +243,12 @@ def make_route_state(
         for address, metadata in address_to_metadata.items()
     }
 
-    return RouteState(
-        route=canonical_path,
-        address_to_metadata=canonical_address_metadata,
-        estimated_fee=estimated_fee,
-    )
+    try:
+        return RouteState(
+            route=canonical_path,
+            address_to_metadata=canonical_address_metadata,
+            estimated_fee=estimated_fee,
+        )
+    except ValueError as ex:
+        log.warning("Invalid metadata in route", error=str(ex))
+        return None
diff --git a/raiden/settings.py b/raiden/settings.py
index 9d97e23ad4..bd94ef3cbb 100644
--- a/raiden/settings.py
+++ b/raiden/settings.py
@@ -5,7 +5,7 @@
 from eth_utils import denoms, to_hex
 
 import raiden_contracts.constants
-from raiden.constants import DISCOVERY_DEFAULT_ROOM, MATRIX_AUTO_SELECT_SERVER, Environment
+from raiden.constants import MATRIX_AUTO_SELECT_SERVER, Environment
 from raiden.network.pathfinding import PFSConfig
 from raiden.utils.typing import (
     Address,
@@ -142,7 +142,6 @@ class MatrixTransportConfig:
     retries_before_backoff: int
     retry_interval_initial: float
     retry_interval_max: float
-    broadcast_rooms: List[str]
     server: str
     available_servers: List[str]
     sync_timeout: int = DEFAULT_TRANSPORT_MATRIX_SYNC_TIMEOUT
@@ -212,7 +211,6 @@ class RaidenConfig:
     transport: MatrixTransportConfig = MatrixTransportConfig(
         # None causes fetching from url in raiden.settings.py::DEFAULT_MATRIX_KNOWN_SERVERS
         available_servers=[],
-        broadcast_rooms=[DISCOVERY_DEFAULT_ROOM],
         retries_before_backoff=DEFAULT_TRANSPORT_RETRIES_BEFORE_BACKOFF,
         retry_interval_initial=DEFAULT_TRANSPORT_MATRIX_RETRY_INTERVAL_INITIAL,
         retry_interval_max=DEFAULT_TRANSPORT_MATRIX_RETRY_INTERVAL_MAX,
diff --git a/raiden/storage/restore.py b/raiden/storage/restore.py
index 011e5c1076..60430a3803 100644
--- a/raiden/storage/restore.py
+++ b/raiden/storage/restore.py
@@ -35,7 +35,7 @@ def channel_state_until_state_change(
     canonical_identifier: CanonicalIdentifier,
     state_change_identifier: StateChangeID,
 ) -> NettingChannelState:  # pragma: no unittest
-    """ Go through WAL state changes until a certain balance hash is found. """
+    """Go through WAL state changes until a certain balance hash is found."""
     assert raiden.wal, "Raiden has not been started yet"
 
     chain_state = restore_state(
diff --git a/raiden/storage/serialization/schemas.py b/raiden/storage/serialization/schemas.py
index e669bfe467..5df8f5b27c 100644
--- a/raiden/storage/serialization/schemas.py
+++ b/raiden/storage/serialization/schemas.py
@@ -14,6 +14,7 @@
     TransferTask,
 )
 from raiden.transfer.events import (
+    ContractSendChannelBatchUnlock,
     ContractSendChannelClose,
     ContractSendChannelSettle,
     ContractSendChannelUpdateTransfer,
@@ -29,7 +30,6 @@
 from raiden.transfer.mediated_transfer.events import (
     SendLockedTransfer,
     SendLockExpired,
-    SendRefundTransfer,
     SendSecretRequest,
     SendSecretReveal,
     SendUnlock,
@@ -91,7 +91,7 @@ def __init__(self, **kwargs: Any) -> None:
 
 
 class BytesField(marshmallow.fields.Field):
-    """ Used for `bytes` in the dataclass, serialize to hex encoding"""
+    """Used for `bytes` in the dataclass, serialize to hex encoding"""
 
     def _serialize(
         self, value: Optional[bytes], attr: Any, obj: Any, **kwargs: Any
@@ -112,7 +112,7 @@ def _deserialize(
 
 
 class AddressField(marshmallow.fields.Field):
-    """ Converts addresses from bytes to hex and vice versa """
+    """Converts addresses from bytes to hex and vice versa"""
 
     def _serialize(self, value: Address, attr: Any, obj: Any, **kwargs: Any) -> str:
         return to_hex_address(value)
@@ -125,7 +125,7 @@ def _deserialize(self, value: str, attr: Any, data: Any, **kwargs: Any) -> Addre
 
 
 class QueueIdentifierField(marshmallow.fields.Field):
-    """ Converts QueueIdentifier objects to a tuple """
+    """Converts QueueIdentifier objects to a tuple"""
 
     @staticmethod
     def _canonical_id_from_string(string: str) -> CanonicalIdentifier:
@@ -167,7 +167,7 @@ def _deserialize(self, value: str, attr: Any, data: Any, **kwargs: Any) -> Queue
 
 
 class PRNGField(marshmallow.fields.Field):
-    """ Serialization for instances of random.Random. """
+    """Serialization for instances of random.Random."""
 
     @staticmethod
     def pseudo_random_generator_from_json(data: Any) -> Random:
@@ -277,7 +277,6 @@ class Meta:
                 SendSecretReveal,
                 SendUnlock,
                 SendSecretRequest,
-                SendRefundTransfer,
                 SendWithdrawRequest,
                 SendWithdrawConfirmation,
                 SendWithdrawExpired,
@@ -291,6 +290,7 @@ class Meta:
                 ContractSendChannelClose,
                 ContractSendChannelSettle,
                 ContractSendChannelUpdateTransfer,
+                ContractSendChannelBatchUnlock,
                 ContractSendSecretReveal,
             ],
             allow_none=False,
diff --git a/raiden/storage/sqlite.py b/raiden/storage/sqlite.py
index 829a55db9b..714e572efa 100644
--- a/raiden/storage/sqlite.py
+++ b/raiden/storage/sqlite.py
@@ -348,7 +348,7 @@ def update_version(self) -> None:
         self.maybe_commit()
 
     def log_run(self) -> None:
-        """ Log timestamp and raiden version to help with debugging """
+        """Log timestamp and raiden version to help with debugging"""
         version = get_system_spec()["raiden"]
         cursor = self.conn.cursor()
         cursor.execute("INSERT INTO runs(raiden_version) VALUES (?)", [version])
@@ -498,7 +498,7 @@ def get_snapshot_before_state_change(
     def get_latest_event_by_data_field(
         self, query: FilteredDBQuery
     ) -> Optional[EventEncodedRecord]:
-        """ Return the latest event filtered query."""
+        """Return the latest event filtered query."""
         cursor = self.conn.cursor()
 
         query_str, args = _query_to_string(query)
@@ -558,7 +558,7 @@ def _form_and_execute_json_query(
     def get_latest_state_change_by_data_field(
         self, query: FilteredDBQuery
     ) -> Optional[StateChangeEncodedRecord]:
-        """ Return all state changes filtered by a named field and value."""
+        """Return all state changes filtered by a named field and value."""
         cursor = self.conn.cursor()
 
         query_str, args = _query_to_string(query)
@@ -966,7 +966,7 @@ def write_events(self, events: List[Tuple[StateChangeID, Event]]) -> List[EventI
     def get_snapshot_before_state_change(
         self, state_change_identifier: StateChangeID
     ) -> Optional[SnapshotRecord]:
-        """ Get snapshots earlier than state_change with provided ID. """
+        """Get snapshots earlier than state_change with provided ID."""
         result: Optional[SnapshotRecord]
 
         row = self.database.get_snapshot_before_state_change(state_change_identifier)
@@ -986,7 +986,7 @@ def get_snapshot_before_state_change(
         return result
 
     def get_latest_event_by_data_field(self, query: FilteredDBQuery) -> Optional[EventRecord]:
-        """ Return all state changes filtered by a named field and value."""
+        """Return all state changes filtered by a named field and value."""
         encoded_event = self.database.get_latest_event_by_data_field(query)
 
         event = None
@@ -1002,7 +1002,7 @@ def get_latest_event_by_data_field(self, query: FilteredDBQuery) -> Optional[Eve
     def get_latest_state_change_by_data_field(
         self, query: FilteredDBQuery
     ) -> Optional[StateChangeRecord]:
-        """ Return all state changes filtered by a named field and value."""
+        """Return all state changes filtered by a named field and value."""
 
         encoded_state_change = self.database.get_latest_state_change_by_data_field(query)
 
diff --git a/raiden/tasks.py b/raiden/tasks.py
index 03603ee549..ccb9ac89d3 100644
--- a/raiden/tasks.py
+++ b/raiden/tasks.py
@@ -77,7 +77,7 @@ def _do_check_version(current_version: Tuple[str, ...], raiden: "RaidenService")
 
 
 def check_version(current_version: str, raiden: "RaidenService") -> None:  # pragma: no unittest
-    """ Check periodically for a new release """
+    """Check periodically for a new release"""
     app_version = parse_version(current_version)
     while True:
         try:
@@ -91,7 +91,7 @@ def check_version(current_version: str, raiden: "RaidenService") -> None:  # pra
 
 
 def check_gas_reserve(raiden: "RaidenService") -> None:  # pragma: no unittest
-    """ Check periodically for gas reserve in the account """
+    """Check periodically for gas reserve in the account"""
     while True:
         has_enough_balance, estimated_required_balance = gas_reserve.has_enough_gas_reserve(
             raiden, channels_to_open=1
@@ -124,7 +124,7 @@ def check_gas_reserve(raiden: "RaidenService") -> None:  # pragma: no unittest
 def check_rdn_deposits(
     raiden: "RaidenService", user_deposit_proxy: UserDeposit
 ) -> None:  # pragma: no unittest
-    """ Check periodically for RDN deposits in the user-deposits contract """
+    """Check periodically for RDN deposits in the user-deposits contract"""
     while True:
         rei_balance = user_deposit_proxy.effective_balance(raiden.address, BLOCK_ID_LATEST)
         rdn_balance = to_rdn(rei_balance)
@@ -149,9 +149,15 @@ def check_rdn_deposits(
 
 
 def check_chain_id(chain_id: ChainID, web3: Web3) -> None:  # pragma: no unittest
-    """ Check periodically if the underlying ethereum client's network id has changed"""
+    """Check periodically if the underlying ethereum client's network id has changed"""
     while True:
-        current_id = web3.eth.chain_id
+        try:
+            current_id = web3.eth.chain_id
+        except requests.exceptions.ConnectionError:
+            raise RuntimeError(
+                "Could not reach ethereum RPC. "
+                "Please check that your ethereum node is running and accessible."
+            )
         if chain_id != current_id:
             raise RuntimeError(
                 f"Raiden was running on network with id {chain_id} and it detected "
@@ -163,7 +169,7 @@ def check_chain_id(chain_id: ChainID, web3: Web3) -> None:  # pragma: no unittes
 
 
 class AlarmTask(Runnable):
-    """ Task to notify when a block is mined. """
+    """Task to notify when a block is mined."""
 
     def __init__(self, proxy_manager: ProxyManager, sleep_time: float) -> None:
         super().__init__()
diff --git a/raiden/transfer/architecture.py b/raiden/transfer/architecture.py
index 368f56bde6..b7bd185701 100644
--- a/raiden/transfer/architecture.py
+++ b/raiden/transfer/architecture.py
@@ -163,14 +163,14 @@ def queue_identifier(self) -> QueueIdentifier:
 
 @dataclass(frozen=True)
 class AuthenticatedSenderStateChange(StateChange):
-    """ Marker used for state changes for which the sender has been verified. """
+    """Marker used for state changes for which the sender has been verified."""
 
     sender: Address
 
 
 @dataclass(frozen=True)
 class ContractSendEvent(Event):
-    """ Marker used for events which represent on-chain transactions. """
+    """Marker used for events which represent on-chain transactions."""
 
     triggered_by_block_hash: BlockHash
 
@@ -189,7 +189,7 @@ class ContractSendExpirableEvent(ContractSendEvent):
 
 @dataclass(frozen=True)
 class ContractReceiveStateChange(StateChange):
-    """ Marker used for state changes which represent on-chain logs. """
+    """Marker used for state changes which represent on-chain logs."""
 
     transaction_hash: TransactionHash
     block_number: BlockNumber
@@ -228,7 +228,7 @@ def __ne__(self, other: Any) -> bool:
 
 @dataclass
 class BalanceProofUnsignedState(State):
-    """ Balance proof from the local node without the signature. """
+    """Balance proof from the local node without the signature."""
 
     nonce: Nonce
     transferred_amount: TokenAmount
diff --git a/raiden/transfer/channel.py b/raiden/transfer/channel.py
index d4187152f1..5acddc4bcb 100644
--- a/raiden/transfer/channel.py
+++ b/raiden/transfer/channel.py
@@ -32,9 +32,7 @@
 from raiden.transfer.mediated_transfer.events import (
     SendLockedTransfer,
     SendLockExpired,
-    SendRefundTransfer,
     SendUnlock,
-    refund_from_sendmediated,
 )
 from raiden.transfer.mediated_transfer.mediation_fee import (
     FeeScheduleState,
@@ -428,7 +426,7 @@ def get_secret(end_state: NettingChannelEndState, secrethash: SecretHash) -> Opt
 def is_balance_proof_safe_for_onchain_operations(
     balance_proof: BalanceProofSignedState,
 ) -> bool:
-    """ Check if the balance proof would overflow onchain. """
+    """Check if the balance proof would overflow onchain."""
     total_amount = balance_proof.transferred_amount + balance_proof.locked_amount
     return total_amount <= UINT256_MAX
 
@@ -1530,49 +1528,6 @@ def send_lockedtransfer(
     return send_locked_transfer_event
 
 
-def send_refundtransfer(
-    channel_state: NettingChannelState,
-    initiator: InitiatorAddress,
-    target: TargetAddress,
-    amount: PaymentWithFeeAmount,
-    message_identifier: MessageID,
-    payment_identifier: PaymentID,
-    expiration: BlockExpiration,
-    secrethash: SecretHash,
-    route_state: RouteState,
-    recipient_metadata: AddressMetadata = None,
-) -> SendRefundTransfer:
-    msg = "Refunds are only valid for *known and pending* transfers"
-    assert secrethash in channel_state.partner_state.secrethashes_to_lockedlocks, msg
-
-    msg = "caller must make sure the channel is open"
-    assert get_status(channel_state) == ChannelState.STATE_OPENED, msg
-
-    send_mediated_transfer, pending_locks = create_sendlockedtransfer(
-        channel_state=channel_state,
-        initiator=initiator,
-        target=target,
-        amount=amount,
-        message_identifier=message_identifier,
-        payment_identifier=payment_identifier,
-        expiration=expiration,
-        secrethash=secrethash,
-        route_states=[route_state],
-        recipient_metadata=recipient_metadata,
-    )
-
-    mediated_transfer = send_mediated_transfer.transfer
-    lock = mediated_transfer.lock
-
-    channel_state.our_state.balance_proof = mediated_transfer.balance_proof
-    channel_state.our_state.nonce = mediated_transfer.balance_proof.nonce
-    channel_state.our_state.pending_locks = pending_locks
-    channel_state.our_state.secrethashes_to_lockedlocks[lock.secrethash] = lock
-
-    refund_transfer = refund_from_sendmediated(send_mediated_transfer)
-    return refund_transfer
-
-
 def send_unlock(
     channel_state: NettingChannelState,
     message_identifier: MessageID,
@@ -1633,6 +1588,7 @@ def send_withdraw_request(
     total_withdraw: WithdrawAmount,
     block_number: BlockNumber,
     pseudo_random_generator: random.Random,
+    recipient_metadata: Optional[AddressMetadata],
 ) -> List[Event]:
     events: List[Event] = list()
 
@@ -1644,7 +1600,10 @@ def send_withdraw_request(
         block_number=block_number, reveal_timeout=channel_state.reveal_timeout
     )
     withdraw_state = PendingWithdrawState(
-        total_withdraw=total_withdraw, nonce=nonce, expiration=expiration
+        total_withdraw=total_withdraw,
+        nonce=nonce,
+        expiration=expiration,
+        recipient_metadata=recipient_metadata,
     )
 
     channel_state.our_state.nonce = nonce
@@ -1657,12 +1616,12 @@ def send_withdraw_request(
             channel_identifier=channel_state.identifier,
         ),
         recipient=channel_state.partner_state.address,
-        recipient_metadata=None,
         message_identifier=message_identifier_from_prng(pseudo_random_generator),
         total_withdraw=withdraw_state.total_withdraw,
         participant=channel_state.our_state.address,
         nonce=channel_state.our_state.nonce,
         expiration=withdraw_state.expiration,
+        recipient_metadata=recipient_metadata,
     )
 
     events.append(withdraw_event)
@@ -1779,7 +1738,10 @@ def send_expired_withdraws(
 
         channel_state.our_state.withdraws_expired.append(
             ExpiredWithdrawState(
-                withdraw_state.total_withdraw, withdraw_state.expiration, withdraw_state.nonce
+                withdraw_state.total_withdraw,
+                withdraw_state.expiration,
+                withdraw_state.nonce,
+                withdraw_state.recipient_metadata,
             )
         )
         del channel_state.our_state.withdraws_pending[withdraw_state.total_withdraw]
@@ -1787,7 +1749,7 @@ def send_expired_withdraws(
         events.append(
             SendWithdrawExpired(
                 recipient=channel_state.partner_state.address,
-                recipient_metadata=None,
+                recipient_metadata=withdraw_state.recipient_metadata,
                 canonical_identifier=channel_state.canonical_identifier,
                 message_identifier=message_identifier_from_prng(pseudo_random_generator),
                 total_withdraw=withdraw_state.total_withdraw,
@@ -1912,6 +1874,7 @@ def handle_action_withdraw(
             total_withdraw=action_withdraw.total_withdraw,
             block_number=block_number,
             pseudo_random_generator=pseudo_random_generator,
+            recipient_metadata=action_withdraw.recipient_metadata,
         )
     else:
         error_msg = is_valid_withdraw.as_error_message
@@ -1963,6 +1926,7 @@ def handle_receive_withdraw_request(
             total_withdraw=withdraw_request.total_withdraw,
             nonce=withdraw_request.nonce,
             expiration=withdraw_request.expiration,
+            recipient_metadata=withdraw_request.sender_metadata,
         )
         channel_state.partner_state.withdraws_pending[
             withdraw_state.total_withdraw
@@ -1973,7 +1937,7 @@ def handle_receive_withdraw_request(
         send_withdraw = SendWithdrawConfirmation(
             canonical_identifier=channel_state.canonical_identifier,
             recipient=channel_state.partner_state.address,
-            recipient_metadata=None,
+            recipient_metadata=withdraw_request.sender_metadata,
             message_identifier=withdraw_request.message_identifier,
             total_withdraw=withdraw_request.total_withdraw,
             participant=channel_state.partner_state.address,
@@ -2003,13 +1967,18 @@ def handle_receive_withdraw_confirmation(
         channel_state=channel_state, received_withdraw=withdraw
     )
 
+    withdraw_state = channel_state.our_state.withdraws_pending.get(withdraw.total_withdraw)
+    recipient_metadata = None
+    if withdraw_state is not None:
+        recipient_metadata = withdraw_state.recipient_metadata
+
     events: List[Event]
     if is_valid:
         channel_state.partner_state.nonce = withdraw.nonce
         events = [
             SendProcessed(
                 recipient=channel_state.partner_state.address,
-                recipient_metadata=None,
+                recipient_metadata=recipient_metadata,
                 message_identifier=withdraw.message_identifier,
                 canonical_identifier=CANONICAL_IDENTIFIER_UNORDERED_QUEUE,
             )
@@ -2076,7 +2045,7 @@ def handle_receive_withdraw_expired(
 
         send_processed = SendProcessed(
             recipient=channel_state.partner_state.address,
-            recipient_metadata=None,
+            recipient_metadata=withdraw_state.recipient_metadata,
             message_identifier=withdraw_expired.message_identifier,
             canonical_identifier=CANONICAL_IDENTIFIER_UNORDERED_QUEUE,
         )
diff --git a/raiden/transfer/events.py b/raiden/transfer/events.py
index 47533c8404..8390c1ce93 100644
--- a/raiden/transfer/events.py
+++ b/raiden/transfer/events.py
@@ -42,7 +42,7 @@
 
 @dataclass(frozen=True)
 class SendWithdrawRequest(SendMessageEvent):
-    """ Event used by node to request a withdraw from channel partner."""
+    """Event used by node to request a withdraw from channel partner."""
 
     total_withdraw: WithdrawAmount
     participant: Address
@@ -60,7 +60,7 @@ def __repr__(self) -> str:
 
 @dataclass(frozen=True)
 class SendWithdrawConfirmation(SendMessageEvent):
-    """ Event used by node to confirm a withdraw for a channel's partner."""
+    """Event used by node to confirm a withdraw for a channel's partner."""
 
     total_withdraw: WithdrawAmount
     participant: Address
@@ -78,7 +78,7 @@ def __repr__(self) -> str:
 
 @dataclass(frozen=True)
 class SendWithdrawExpired(SendMessageEvent):
-    """ Event used by node to expire a withdraw request."""
+    """Event used by node to expire a withdraw request."""
 
     total_withdraw: WithdrawAmount
     participant: Address
@@ -96,7 +96,7 @@ def __repr__(self) -> str:
 
 @dataclass(frozen=True)
 class ContractSendChannelWithdraw(ContractSendEvent):
-    """ Event emitted if node wants to withdraw from current channel balance. """
+    """Event emitted if node wants to withdraw from current channel balance."""
 
     canonical_identifier: CanonicalIdentifier
     total_withdraw: WithdrawAmount
@@ -142,7 +142,7 @@ def channel_identifier(self) -> ChannelID:
 
 @dataclass(frozen=True)
 class ContractSendChannelSettle(ContractSendEvent):
-    """ Event emitted if the netting channel must be settled. """
+    """Event emitted if the netting channel must be settled."""
 
     canonical_identifier: CanonicalIdentifier
 
@@ -157,7 +157,7 @@ def channel_identifier(self) -> ChannelID:
 
 @dataclass(frozen=True)
 class ContractSendChannelUpdateTransfer(ContractSendExpirableEvent):
-    """ Event emitted if the netting channel balance proof must be updated. """
+    """Event emitted if the netting channel balance proof must be updated."""
 
     balance_proof: BalanceProofSignedState
 
@@ -201,7 +201,7 @@ def channel_identifier(self) -> ChannelID:
 
 @dataclass(repr=False, frozen=True)
 class ContractSendSecretReveal(ContractSendExpirableEvent):
-    """ Event emitted when the lock must be claimed on-chain. """
+    """Event emitted when the lock must be claimed on-chain."""
 
     secret: Secret = field(repr=False)
 
@@ -336,7 +336,7 @@ def __repr__(self) -> str:
 
 @dataclass(frozen=True)
 class EventInvalidReceivedTransferRefund(Event):
-    """ Event emitted when an invalid refund transfer is received. """
+    """Event emitted when an invalid refund transfer is received."""
 
     payment_identifier: PaymentID
     reason: str
@@ -344,7 +344,7 @@ class EventInvalidReceivedTransferRefund(Event):
 
 @dataclass(frozen=True)
 class EventInvalidReceivedLockExpired(Event):
-    """ Event emitted when an invalid lock expired message is received. """
+    """Event emitted when an invalid lock expired message is received."""
 
     secrethash: SecretHash
     reason: str
@@ -360,7 +360,7 @@ def __repr__(self) -> str:
 
 @dataclass(frozen=True)
 class EventInvalidReceivedLockedTransfer(Event):
-    """ Event emitted when an invalid locked transfer is received. """
+    """Event emitted when an invalid locked transfer is received."""
 
     payment_identifier: PaymentID
     reason: str
@@ -368,7 +368,7 @@ class EventInvalidReceivedLockedTransfer(Event):
 
 @dataclass(frozen=True)
 class EventInvalidReceivedUnlock(Event):
-    """ Event emitted when an invalid unlock message is received. """
+    """Event emitted when an invalid unlock message is received."""
 
     secrethash: SecretHash
     reason: str
@@ -384,7 +384,7 @@ def __repr__(self) -> str:
 
 @dataclass(frozen=True)
 class EventInvalidReceivedWithdrawRequest(Event):
-    """ Event emitted when an invalid withdraw request is received. """
+    """Event emitted when an invalid withdraw request is received."""
 
     attempted_withdraw: WithdrawAmount
     reason: str
@@ -392,7 +392,7 @@ class EventInvalidReceivedWithdrawRequest(Event):
 
 @dataclass(frozen=True)
 class EventInvalidReceivedWithdraw(Event):
-    """ Event emitted when an invalid withdraw confirmation is received. """
+    """Event emitted when an invalid withdraw confirmation is received."""
 
     attempted_withdraw: WithdrawAmount
     reason: str
@@ -400,7 +400,7 @@ class EventInvalidReceivedWithdraw(Event):
 
 @dataclass(frozen=True)
 class EventInvalidReceivedWithdrawExpired(Event):
-    """ Event emitted when an invalid withdraw expired event is received. """
+    """Event emitted when an invalid withdraw expired event is received."""
 
     attempted_withdraw: WithdrawAmount
     reason: str
@@ -408,7 +408,7 @@ class EventInvalidReceivedWithdrawExpired(Event):
 
 @dataclass(frozen=True)
 class EventInvalidActionWithdraw(Event):
-    """ Event emitted when an invalid withdraw is initiated. """
+    """Event emitted when an invalid withdraw is initiated."""
 
     attempted_withdraw: WithdrawAmount
     reason: str
@@ -416,7 +416,7 @@ class EventInvalidActionWithdraw(Event):
 
 @dataclass(frozen=True)
 class EventInvalidActionSetRevealTimeout(Event):
-    """ Event emitted when an invalid withdraw is initiated. """
+    """Event emitted when an invalid withdraw is initiated."""
 
     reveal_timeout: BlockTimeout
     reason: str
@@ -429,7 +429,7 @@ class SendProcessed(SendMessageEvent):
 
 @dataclass(frozen=True)
 class EventInvalidSecretRequest(Event):
-    """ Event emitted when an invalid SecretRequest is received. """
+    """Event emitted when an invalid SecretRequest is received."""
 
     payment_identifier: PaymentID
     intended_amount: PaymentAmount
diff --git a/raiden/transfer/mediated_transfer/events.py b/raiden/transfer/mediated_transfer/events.py
index 59fa726291..55464d4d7d 100644
--- a/raiden/transfer/mediated_transfer/events.py
+++ b/raiden/transfer/mediated_transfer/events.py
@@ -20,18 +20,6 @@
 )
 
 
-def refund_from_sendmediated(
-    send_lockedtransfer_event: "SendLockedTransfer",
-) -> "SendRefundTransfer":
-    return SendRefundTransfer(
-        recipient=send_lockedtransfer_event.recipient,
-        recipient_metadata=send_lockedtransfer_event.recipient_metadata,
-        message_identifier=send_lockedtransfer_event.message_identifier,
-        transfer=send_lockedtransfer_event.transfer,
-        canonical_identifier=send_lockedtransfer_event.queue_identifier.canonical_identifier,
-    )
-
-
 @dataclass(frozen=True)
 class SendLockExpired(SendMessageEvent):
     balance_proof: BalanceProofUnsignedState
@@ -40,7 +28,7 @@ class SendLockExpired(SendMessageEvent):
 
 @dataclass(frozen=True)
 class SendLockedTransfer(SendMessageEvent):
-    """ A locked transfer that must be sent to `recipient`. """
+    """A locked transfer that must be sent to `recipient`."""
 
     transfer: LockedTransferUnsignedState
 
@@ -130,24 +118,9 @@ class SendSecretRequest(SendMessageEvent):
     secrethash: SecretHash
 
 
-@dataclass(frozen=True)
-class SendRefundTransfer(SendMessageEvent):
-    """Event used to cleanly backtrack the current node in the route.
-    This message will pay back the same amount of token from the recipient to
-    the sender, allowing the sender to try a different route without the risk
-    of losing token.
-    """
-
-    transfer: LockedTransferUnsignedState
-
-    @property
-    def balance_proof(self) -> BalanceProofUnsignedState:
-        return self.transfer.balance_proof
-
-
 @dataclass(frozen=True)
 class EventUnlockSuccess(Event):
-    """ Event emitted when a lock unlock succeded. """
+    """Event emitted when a lock unlock succeded."""
 
     identifier: PaymentID
     secrethash: SecretHash
@@ -155,7 +128,7 @@ class EventUnlockSuccess(Event):
 
 @dataclass(frozen=True)
 class EventUnlockFailed(Event):
-    """ Event emitted when a lock unlock failed. """
+    """Event emitted when a lock unlock failed."""
 
     identifier: PaymentID
     secrethash: SecretHash
@@ -164,7 +137,7 @@ class EventUnlockFailed(Event):
 
 @dataclass(frozen=True)
 class EventUnlockClaimSuccess(Event):
-    """ Event emitted when a lock claim succeded. """
+    """Event emitted when a lock claim succeded."""
 
     identifier: PaymentID
     secrethash: SecretHash
@@ -172,7 +145,7 @@ class EventUnlockClaimSuccess(Event):
 
 @dataclass(frozen=True)
 class EventUnlockClaimFailed(Event):
-    """ Event emitted when a lock claim failed. """
+    """Event emitted when a lock claim failed."""
 
     identifier: PaymentID
     secrethash: SecretHash
@@ -181,7 +154,7 @@ class EventUnlockClaimFailed(Event):
 
 @dataclass(frozen=True)
 class EventUnexpectedSecretReveal(Event):
-    """ Event emitted when an unexpected secret reveal message is received. """
+    """Event emitted when an unexpected secret reveal message is received."""
 
     secrethash: SecretHash
     reason: str
diff --git a/raiden/transfer/mediated_transfer/initiator.py b/raiden/transfer/mediated_transfer/initiator.py
index e48faed854..7e41c27d7e 100644
--- a/raiden/transfer/mediated_transfer/initiator.py
+++ b/raiden/transfer/mediated_transfer/initiator.py
@@ -102,7 +102,7 @@ def events_for_unlock_lock(
     pseudo_random_generator: random.Random,
     block_number: BlockNumber,
 ) -> List[Event]:
-    """ Unlocks the lock offchain, and emits the events for the successful payment. """
+    """Unlocks the lock offchain, and emits the events for the successful payment."""
     # next hop learned the secret, unlock the token locally and send the
     # lock claim message to next hop
     transfer_description = initiator_state.transfer_description
@@ -183,7 +183,7 @@ def handle_block(
             events.extend(expired_lock_events)
 
         if initiator_state.received_secret_request:
-            reason = "bad secret request message from target"
+            reason = "lock expired, despite receiving secret request"
         else:
             reason = "lock expired"
 
@@ -326,7 +326,7 @@ def send_lockedtransfer(
     route_state: RouteState,
     route_states: List[RouteState],
 ) -> SendLockedTransfer:
-    """ Create a mediated transfer using channel. """
+    """Create a mediated transfer using channel."""
     assert (
         channel_state.token_network_address == transfer_description.token_network_address
     ), "token_network_address mismatch"
diff --git a/raiden/transfer/mediated_transfer/initiator_manager.py b/raiden/transfer/mediated_transfer/initiator_manager.py
index 93260bbe57..f228ec56e8 100644
--- a/raiden/transfer/mediated_transfer/initiator_manager.py
+++ b/raiden/transfer/mediated_transfer/initiator_manager.py
@@ -65,7 +65,7 @@ def cancel_other_transfers(payment_state: InitiatorPaymentState) -> None:
 
 
 def can_cancel(initiator: InitiatorTransferState) -> bool:
-    """ A transfer is only cancellable until the secret is revealed. """
+    """A transfer is only cancellable until the secret is revealed."""
     return initiator.transfer_state != "transfer_secret_revealed"
 
 
@@ -206,7 +206,7 @@ def handle_cancelpayment(
     payment_state: InitiatorPaymentState,
     channelidentifiers_to_channels: Dict[ChannelID, NettingChannelState],
 ) -> TransitionResult[InitiatorPaymentState]:
-    """ Cancel the payment and all related transfers. """
+    """Cancel the payment and all related transfers."""
     # Cannot cancel a transfer after the secret is revealed
     events = list()
     for initiator_state in payment_state.initiator_transfers.values():
diff --git a/raiden/transfer/mediated_transfer/mediation_fee.py b/raiden/transfer/mediated_transfer/mediation_fee.py
index cf2114a692..a3e7bf021e 100644
--- a/raiden/transfer/mediated_transfer/mediation_fee.py
+++ b/raiden/transfer/mediated_transfer/mediation_fee.py
@@ -89,7 +89,7 @@ def _collect_x_values(
 def _cap_fees(
     x_list: List[Fraction], y_list: List[Fraction]
 ) -> Tuple[List[Fraction], List[Fraction]]:
-    """ Insert extra points for intersections with x-axis, see `test_fee_capping` """
+    """Insert extra points for intersections with x-axis, see `test_fee_capping`"""
     x_list = copy(x_list)
     y_list = copy(y_list)
 
@@ -208,7 +208,7 @@ def mediation_fee_func(
         amount_with_fees: PaymentWithFeeAmount,
         cap_fees: bool,
     ) -> Interpolate:
-        """ Returns a function which calculates total_mediation_fee(amount_without_fees) """
+        """Returns a function which calculates total_mediation_fee(amount_without_fees)"""
         return _mediation_fee_func(
             schedule_in=schedule_in,
             schedule_out=schedule_out,
@@ -230,7 +230,7 @@ def mediation_fee_backwards_func(
         amount_without_fees: PaymentWithFeeAmount,
         cap_fees: bool,
     ) -> Interpolate:
-        """ Returns a function which calculates total_mediation_fee(amount_with_fees) """
+        """Returns a function which calculates total_mediation_fee(amount_with_fees)"""
         return _mediation_fee_func(
             schedule_in=schedule_in,
             schedule_out=schedule_out,
@@ -244,7 +244,7 @@ def mediation_fee_backwards_func(
 
 
 def linspace(start: TokenAmount, stop: TokenAmount, num: int) -> List[TokenAmount]:
-    """ Returns a list of num numbers from start to stop (inclusive). """
+    """Returns a list of num numbers from start to stop (inclusive)."""
     assert num > 1, "Must generate at least one step"
     assert start <= stop, "start must be smaller than stop"
 
diff --git a/raiden/transfer/mediated_transfer/mediator.py b/raiden/transfer/mediated_transfer/mediator.py
index 4b275bf6e2..f9d536ccf7 100644
--- a/raiden/transfer/mediated_transfer/mediator.py
+++ b/raiden/transfer/mediated_transfer/mediator.py
@@ -16,7 +16,6 @@
     EventUnlockFailed,
     EventUnlockSuccess,
     SendLockedTransfer,
-    SendRefundTransfer,
     SendSecretReveal,
 )
 from raiden.transfer.mediated_transfer.mediation_fee import FeeScheduleState, Interpolate
@@ -85,7 +84,7 @@
 
 
 def is_lock_valid(expiration: BlockExpiration, block_number: BlockNumber) -> bool:
-    """ True if the lock has not expired. """
+    """True if the lock has not expired."""
     return block_number <= BlockNumber(expiration)
 
 
@@ -120,7 +119,7 @@ def is_send_transfer_almost_equal(
     send: LockedTransferUnsignedState,
     received: LockedTransferSignedState,
 ) -> bool:
-    """ True if both transfers are for the same mediated transfer. """
+    """True if both transfers are for the same mediated transfer."""
     # The only thing that may change is the direction of the transfer
     return (
         send.payment_identifier == received.payment_identifier
@@ -165,7 +164,7 @@ def get_payee_channel(
     channelidentifiers_to_channels: Dict[ChannelID, NettingChannelState],
     transfer_pair: MediationPairState,
 ) -> Optional[NettingChannelState]:
-    """ Returns the payee channel of a given transfer pair or None if it's not found """
+    """Returns the payee channel of a given transfer pair or None if it's not found"""
     payee_channel_identifier = transfer_pair.payee_transfer.balance_proof.channel_identifier
     return channelidentifiers_to_channels.get(payee_channel_identifier)
 
@@ -174,7 +173,7 @@ def get_payer_channel(
     channelidentifiers_to_channels: Dict[ChannelID, NettingChannelState],
     transfer_pair: MediationPairState,
 ) -> Optional[NettingChannelState]:
-    """ Returns the payer channel of a given transfer pair or None if it's not found """
+    """Returns the payer channel of a given transfer pair or None if it's not found"""
     payer_channel_identifier = transfer_pair.payer_transfer.balance_proof.channel_identifier
     return channelidentifiers_to_channels.get(payer_channel_identifier)
 
@@ -182,7 +181,7 @@ def get_payer_channel(
 def get_pending_transfer_pairs(
     transfers_pair: List[MediationPairState],
 ) -> List[MediationPairState]:
-    """ Return the transfer pairs that are not at a final state. """
+    """Return the transfer pairs that are not at a final state."""
     pending_pairs = [
         pair
         for pair in transfers_pair
@@ -228,7 +227,7 @@ def get_amount_without_fees(
     channel_in: NettingChannelState,
     channel_out: NettingChannelState,
 ) -> Optional[PaymentWithFeeAmount]:
-    """ Return the amount after fees are taken. """
+    """Return the amount after fees are taken."""
 
     balance_in = get_balance(channel_in.our_state, channel_in.partner_state)
     balance_out = get_balance(channel_out.our_state, channel_out.partner_state)
@@ -268,7 +267,7 @@ def sanity_check(
     state: MediatorTransferState,
     channelidentifiers_to_channels: Dict[ChannelID, NettingChannelState],
 ) -> None:
-    """ Check invariants that must hold. """
+    """Check invariants that must hold."""
 
     # if a transfer is paid we must know the secret
     all_transfers_states = itertools.chain(
@@ -441,78 +440,13 @@ def forward_transfer_pair(
     return transfer_pair, mediated_events
 
 
-def backward_transfer_pair(
-    backward_channel: NettingChannelState,
-    payer_transfer: LockedTransferSignedState,
-    pseudo_random_generator: random.Random,
-    block_number: BlockNumber,
-) -> Tuple[Optional[MediationPairState], List[Event]]:
-    """Sends a transfer backwards, allowing the previous hop to try a new
-    route.
-
-    When all the routes available for this node failed, send a transfer
-    backwards with the same amount and secrethash, allowing the previous hop to
-    do a retry.
-
-    Args:
-        backward_channel: The original channel which sent the mediated transfer
-            to this node.
-        payer_transfer: The *latest* payer transfer which is backing the
-            mediation.
-        block_number: The current block number.
-
-    Returns:
-        The mediator pair and the corresponding refund event.
-    """
-    transfer_pair = None
-    events: List[Event] = list()
-
-    lock = payer_transfer.lock
-    lock_timeout = BlockTimeout(lock.expiration - block_number)
-
-    # Ensure the refund transfer's lock has a safe expiration, otherwise don't
-    # do anything and wait for the received lock to expire.
-    if channel.is_channel_usable_for_mediation(backward_channel, lock.amount, lock_timeout):
-        message_identifier = message_identifier_from_prng(pseudo_random_generator)
-
-        backward_route_state = RouteState(
-            route=[backward_channel.our_state.address], address_to_metadata={}
-        )
-
-        refund_transfer = channel.send_refundtransfer(
-            channel_state=backward_channel,
-            initiator=payer_transfer.initiator,
-            target=payer_transfer.target,
-            # `amount` should be `get_lock_amount_after_fees(...)`, but fees
-            # for refunds are currently not defined, so we assume fee=0 to keep
-            # it simple, for now.
-            amount=lock.amount,
-            message_identifier=message_identifier,
-            payment_identifier=payer_transfer.payment_identifier,
-            expiration=lock.expiration,
-            secrethash=lock.secrethash,
-            route_state=backward_route_state,
-            recipient_metadata=payer_transfer.payer_address_metadata,
-        )
-
-        transfer_pair = MediationPairState(
-            payer_transfer=payer_transfer,
-            payee_address=backward_channel.partner_state.address,
-            payee_transfer=refund_transfer.transfer,
-        )
-
-        events.append(refund_transfer)
-
-    return transfer_pair, events
-
-
 def set_offchain_secret(
     state: MediatorTransferState,
     channelidentifiers_to_channels: Dict[ChannelID, NettingChannelState],
     secret: Secret,
     secrethash: SecretHash,
 ) -> List[Event]:
-    """ Set the secret to all mediated transfers. """
+    """Set the secret to all mediated transfers."""
     state.secret = secret
 
     for pair in state.transfers_pair:
@@ -607,7 +541,7 @@ def set_onchain_secret(
 def set_offchain_reveal_state(
     transfers_pair: List[MediationPairState], payee_address: Address
 ) -> None:
-    """ Set the state of a transfer *sent* to a payee. """
+    """Set the state of a transfer *sent* to a payee."""
     for pair in transfers_pair:
         if pair.payee_address == payee_address:
             pair.payee_state = "payee_secret_revealed"
@@ -619,7 +553,7 @@ def events_for_expired_pairs(
     waiting_transfer: Optional[WaitingTransferState],
     block_number: BlockNumber,
 ) -> List[Event]:
-    """ Informational events for expired locks. """
+    """Informational events for expired locks."""
     pending_transfers_pairs = get_pending_transfer_pairs(transfers_pair)
 
     events: List[Event] = list()
@@ -740,7 +674,7 @@ def events_for_balanceproof(
     secret: Secret,
     secrethash: SecretHash,
 ) -> List[Event]:
-    """ While it's safe do the off-chain unlock. """
+    """While it's safe do the off-chain unlock."""
 
     events: List[Event] = list()
     for pair in reversed(transfers_pair):
@@ -936,10 +870,15 @@ def events_to_remove_expired_locks(
     """
     events: List[Event] = list()
 
+    if not mediator_state.transfers_pair:
+        return events
+
+    # This is the initial transfer we received for this payment. Here all the relevant
+    # route and address-metadata is present
+    initial_payer_transfer: LockedTransferSignedState = mediator_state.transfers_pair[
+        0
+    ].payer_transfer
     for transfer_pair in mediator_state.transfers_pair:
-        # XXX check the correct balance proof / channel-state is retrieved
-        # FIXME the balance proof of the payer_transfer and the payee_transfer have
-        #   the same channel_identifier!
         balance_proof = transfer_pair.payee_transfer.balance_proof
         channel_identifier = balance_proof.channel_identifier
         channel_state = channelidentifiers_to_channels.get(channel_identifier)
@@ -966,8 +905,11 @@ def events_to_remove_expired_locks(
 
             is_channel_open = channel.get_status(channel_state) == ChannelState.STATE_OPENED
 
+            # The initial_payer_transfer is the initial transfer we received from a previous hop,
+            # so there we have all metadata that was passed on to other nodes already
+            # present
             payee_address_metadata = get_address_metadata(
-                transfer_pair.payee_address, transfer_pair.payee_transfer.route_states
+                transfer_pair.payee_address, initial_payer_transfer.route_states
             )
             if has_lock_expired and is_channel_open:
                 transfer_pair.payee_state = "payee_expired"
@@ -1092,30 +1034,7 @@ def mediate_transfer(
             state.transfers_pair.append(mediation_transfer_pair)
             return TransitionResult(state, mediation_events)
 
-    # Could not mediate, try to refund
-    if state.transfers_pair:
-        original_pair = state.transfers_pair[0]
-        original_channel = addresses_to_channel.get(
-            (
-                original_pair.payer_transfer.balance_proof.token_network_address,
-                original_pair.payer_transfer.payer_address,
-            )
-        )
-    else:
-        original_channel = payer_channel
-
-    if original_channel:
-        refund_transfer_pair, refund_events = backward_transfer_pair(
-            original_channel,
-            payer_transfer,
-            pseudo_random_generator,
-            block_number,
-        )
-        if refund_transfer_pair:
-            state.transfers_pair.append(refund_transfer_pair)
-            return TransitionResult(state, refund_events)
-
-    # Neither mediation nor refund possible, wait for an opportunity to do either
+    # Could not mediate, wait for a later time to do so
     state.waiting_transfer = WaitingTransferState(payer_transfer)
     return TransitionResult(state, [])
 
@@ -1199,7 +1118,7 @@ def handle_block(
             mediator_state = mediation_attempt.new_state
             mediate_events = mediation_attempt.events
             success_filter = lambda event: (
-                isinstance(event, (SendLockedTransfer, SendRefundTransfer))
+                isinstance(event, SendLockedTransfer)
                 and event.transfer.lock.secrethash == secrethash
             )
 
@@ -1307,12 +1226,17 @@ def handle_offchain_secretreveal(
     block_number: BlockNumber,
     block_hash: BlockHash,
 ) -> TransitionResult[MediatorTransferState]:
-    """ Handles the secret reveal and sends SendUnlock/RevealSecret if necessary. """
+    """Handles the secret reveal and sends SendUnlock/RevealSecret if necessary."""
     is_valid_reveal = is_valid_secret_reveal(
         state_change=mediator_state_change, transfer_secrethash=mediator_state.secrethash
     )
     is_secret_unknown = mediator_state.secret is None
 
+    if not mediator_state.transfers_pair:
+        # This will not happen during normal operation, but attackers might
+        # send weird messages.
+        return TransitionResult(mediator_state, list())
+
     # a SecretReveal should be rejected if the payer transfer
     # has expired. To check for this, we use the last
     # transfer pair.
@@ -1396,7 +1320,7 @@ def handle_unlock(
     state_change: ReceiveUnlock,
     channelidentifiers_to_channels: Dict[ChannelID, NettingChannelState],
 ) -> TransitionResult[MediatorTransferState]:
-    """ Handle a ReceiveUnlock state change. """
+    """Handle a ReceiveUnlock state change."""
     events = list()
     balance_proof_sender = state_change.balance_proof.sender
     channel_identifier = state_change.balance_proof.channel_identifier
@@ -1482,7 +1406,7 @@ def state_transition(
     block_number: BlockNumber,
     block_hash: BlockHash,
 ) -> TransitionResult[Optional[MediatorTransferState]]:
-    """ State machine for a node mediating a transfer. """
+    """State machine for a node mediating a transfer."""
     # pylint: disable=too-many-branches
     # Notes:
     # - A user cannot cancel a mediated transfer after it was initiated, she
diff --git a/raiden/transfer/mediated_transfer/state.py b/raiden/transfer/mediated_transfer/state.py
index a5f7dfda24..aba940d5c7 100644
--- a/raiden/transfer/mediated_transfer/state.py
+++ b/raiden/transfer/mediated_transfer/state.py
@@ -147,7 +147,7 @@ class WaitingTransferState(State):
 
 @dataclass
 class InitiatorTransferState(State):
-    """ State of a transfer for the initiator node. """
+    """State of a transfer for the initiator node."""
 
     route: RouteState
     transfer_description: TransferDescriptionWithSecretState = field(repr=False)
@@ -247,7 +247,7 @@ class MediatorTransferState(State):
 
 @dataclass
 class TargetTransferState(State):
-    """ State of a transfer for the target node. """
+    """State of a transfer for the target node."""
 
     EXPIRED = "expired"
     OFFCHAIN_SECRET_REVEAL = "reveal_secret"
diff --git a/raiden/transfer/mediated_transfer/state_change.py b/raiden/transfer/mediated_transfer/state_change.py
index 5816707ce8..96c6280466 100644
--- a/raiden/transfer/mediated_transfer/state_change.py
+++ b/raiden/transfer/mediated_transfer/state_change.py
@@ -28,7 +28,7 @@
 # useful work, ie. there must /not/ be an event for requesting new data.
 @dataclass(frozen=True)
 class ActionInitInitiator(StateChange):
-    """ Initial state of a new mediated transfer. """
+    """Initial state of a new mediated transfer."""
 
     transfer: TransferDescriptionWithSecretState
     routes: List[RouteState]
@@ -98,14 +98,14 @@ def __post_init__(self) -> None:
 
 @dataclass(frozen=True)
 class ReceiveTransferCancelRoute(BalanceProofStateChange):
-    """ A mediator sends us a refund due to a failed route """
+    """A mediator sends us a refund due to a failed route"""
 
     transfer: LockedTransferSignedState
 
 
 @dataclass(frozen=True)
 class ReceiveLockExpired(BalanceProofStateChange):
-    """ A LockExpired message received. """
+    """A LockExpired message received."""
 
     secrethash: SecretHash
     message_identifier: MessageID
@@ -113,7 +113,7 @@ class ReceiveLockExpired(BalanceProofStateChange):
 
 @dataclass(frozen=True)
 class ReceiveSecretRequest(AuthenticatedSenderStateChange):
-    """ A SecretRequest message received. """
+    """A SecretRequest message received."""
 
     payment_identifier: PaymentID
     amount: PaymentAmount
@@ -124,7 +124,7 @@ class ReceiveSecretRequest(AuthenticatedSenderStateChange):
 
 @dataclass(frozen=True)
 class ReceiveSecretReveal(AuthenticatedSenderStateChange):
-    """ A SecretReveal message received. """
+    """A SecretReveal message received."""
 
     secret: Secret = field(repr=False)
     secrethash: SecretHash = field(default=EMPTY_SECRETHASH)
@@ -135,7 +135,7 @@ def __post_init__(self) -> None:
 
 @dataclass(frozen=True)
 class ReceiveTransferRefund(BalanceProofStateChange):
-    """ A RefundTransfer message received. """
+    """A RefundTransfer message received."""
 
     transfer: LockedTransferSignedState
 
diff --git a/raiden/transfer/mediated_transfer/target.py b/raiden/transfer/mediated_transfer/target.py
index 21a39eb139..206b7a1beb 100644
--- a/raiden/transfer/mediated_transfer/target.py
+++ b/raiden/transfer/mediated_transfer/target.py
@@ -91,7 +91,7 @@ def handle_inittarget(
     pseudo_random_generator: random.Random,
     block_number: BlockNumber,
 ) -> TransitionResult[Optional[TargetTransferState]]:
-    """ Handles an ActionInitTarget state change. """
+    """Handles an ActionInitTarget state change."""
     iteration: TransitionResult[Optional[TargetTransferState]]
     transfer = state_change.transfer
     from_hop = state_change.from_hop
@@ -160,7 +160,7 @@ def handle_offchain_secretreveal(
     pseudo_random_generator: random.Random,
     block_number: BlockNumber,
 ) -> TransitionResult[TargetTransferState]:
-    """ Validates and handles a ReceiveSecretReveal state change. """
+    """Validates and handles a ReceiveSecretReveal state change."""
     valid_secret = is_valid_secret_reveal(
         state_change=state_change, transfer_secrethash=target_state.transfer.lock.secrethash
     )
@@ -203,7 +203,7 @@ def handle_onchain_secretreveal(
     state_change: ContractReceiveSecretReveal,
     channel_state: NettingChannelState,
 ) -> TransitionResult[TargetTransferState]:
-    """ Validates and handles a ContractReceiveSecretReveal state change. """
+    """Validates and handles a ContractReceiveSecretReveal state change."""
     valid_secret = is_valid_secret_reveal(
         state_change=state_change, transfer_secrethash=target_state.transfer.lock.secrethash
     )
@@ -227,7 +227,7 @@ def handle_unlock(
     state_change: ReceiveUnlock,
     channel_state: NettingChannelState,
 ) -> TransitionResult[Optional[TargetTransferState]]:
-    """ Handles a ReceiveUnlock state change. """
+    """Handles a ReceiveUnlock state change."""
 
     recipient_metadata = target_state.transfer.payer_address_metadata
     is_valid, events, _ = channel.handle_unlock(channel_state, state_change, recipient_metadata)
@@ -329,7 +329,7 @@ def state_transition(
     pseudo_random_generator: random.Random,
     block_number: BlockNumber,
 ) -> TransitionResult[Optional[TargetTransferState]]:
-    """ State machine for the target node of a mediated transfer. """
+    """State machine for the target node of a mediated transfer."""
     # pylint: disable=too-many-branches,unidiomatic-typecheck
 
     iteration = TransitionResult(target_state, list())
diff --git a/raiden/transfer/node.py b/raiden/transfer/node.py
index f079303fca..7a715fb434 100644
--- a/raiden/transfer/node.py
+++ b/raiden/transfer/node.py
@@ -430,7 +430,7 @@ def inplace_delete_message_queue(
     state_change: Union[ReceiveDelivered, ReceiveProcessed, ReceiveWithdrawConfirmation],
     queueid: QueueIdentifier,
 ) -> None:
-    """ Filter messages from queue, if the queue becomes empty, cleanup the queue itself. """
+    """Filter messages from queue, if the queue becomes empty, cleanup the queue itself."""
     queue = chain_state.queueids_to_queues.get(queueid)
     if not queue:
         if queueid in chain_state.queueids_to_queues:
@@ -449,7 +449,7 @@ def inplace_delete_message(
     message_queue: List[SendMessageEvent],
     state_change: Union[ReceiveDelivered, ReceiveProcessed, ReceiveWithdrawConfirmation],
 ) -> None:
-    """ Check if the message exists in queue with ID `queueid` and exclude if found."""
+    """Check if the message exists in queue with ID `queueid` and exclude if found."""
     for message in list(message_queue):
         # A withdraw request is only confirmed by a withdraw confirmation.
         # This is done because Processed is not an indicator that the partner has
@@ -536,7 +536,7 @@ def handle_contract_receive_channel_closed(
 def handle_receive_delivered(
     chain_state: ChainState, state_change: ReceiveDelivered
 ) -> TransitionResult[ChainState]:
-    """ Check if the "Delivered" message exists in the global queue and delete if found."""
+    """Check if the "Delivered" message exists in the global queue and delete if found."""
     queueid = QueueIdentifier(state_change.sender, CANONICAL_IDENTIFIER_UNORDERED_QUEUE)
     inplace_delete_message_queue(chain_state, state_change, queueid)
     return TransitionResult(chain_state, [])
diff --git a/raiden/transfer/routes.py b/raiden/transfer/routes.py
index e64ce03ba5..803d52e951 100644
--- a/raiden/transfer/routes.py
+++ b/raiden/transfer/routes.py
@@ -7,7 +7,7 @@
 def filter_reachable_routes(
     route_states: List[RouteState], nodeaddresses_to_networkstates: NodeNetworkStateMap
 ) -> List[RouteState]:
-    """ This function makes sure we use reachable routes only. """
+    """This function makes sure we use reachable routes only."""
 
     return [
         route
@@ -27,7 +27,7 @@ def filter_acceptable_routes(
     addresses_to_channel: Dict[Tuple[TokenNetworkAddress, Address], NettingChannelState],
     token_network_address: TokenNetworkAddress,
 ) -> List[RouteState]:
-    """ Keeps only routes whose forward_channel is not in the list of blacklisted channels """
+    """Keeps only routes whose forward_channel is not in the list of blacklisted channels"""
 
     acceptable_routes = list()
     for route in route_states:
@@ -47,6 +47,10 @@ def prune_route_table(
     contains only routes using the same forward channel and removes our own
     address in the process.
     Note that address metadata are kept complete for the whole route.
+
+    Also note that we don't need to handle ``ValueError``s here since the new
+    ``RouteState``s are built from existing ones, which means the metadata have
+    already been validated.
     """
 
     return [
diff --git a/raiden/transfer/state.py b/raiden/transfer/state.py
index 8818772e4f..53aabff252 100644
--- a/raiden/transfer/state.py
+++ b/raiden/transfer/state.py
@@ -65,6 +65,8 @@
     WithdrawAmount,
     typecheck,
 )
+from raiden.utils.validation import validate_address_metadata
+
 
 QueueIdsToQueues = Dict[QueueIdentifier, List[SendMessageEvent]]
 
@@ -119,7 +121,7 @@ class PaymentMappingState(State):
 
 @dataclass
 class HopState(State):
-    """ Information about the next hop. """
+    """Information about the next hop."""
 
     node_address: Address
     channel_identifier: ChannelID
@@ -130,7 +132,7 @@ def __post_init__(self) -> None:
 
 @dataclass
 class RouteState(State):
-    """ A possible route for a payment to a given target. """
+    """A possible route for a payment to a given target."""
 
     # TODO: Add timestamp
     route: List[Address]
@@ -138,6 +140,22 @@ class RouteState(State):
     swaps: Dict[Address, TokenNetworkAddress] = field(default_factory=dict)
     estimated_fee: FeeAmount = FeeAmount(0)
 
+    def __post_init__(self) -> None:
+        """Validate address_to_metadata.
+
+        Currently any validation error will cause a :class:``ValueError`` to be raised.
+        This needs to be caught in higher layers.
+
+        In the future a more granular validation may be introduced.
+        """
+        validation_errors = validate_address_metadata(self)
+        if validation_errors:
+            addresses_with_errors = ", ".join(
+                f"{to_checksum_address(address)}: {errors}"
+                for address, errors in validation_errors.items()
+            )
+            raise ValueError(f"Could not validate metadata {addresses_with_errors}.")
+
     @property
     def next_hop_address(self) -> Address:
         assert len(self.route) >= 1, "Route has no next hop"
@@ -163,7 +181,7 @@ def __repr__(self) -> str:
 
 @dataclass
 class HashTimeLockState(State):
-    """ Represents a hash time lock. """
+    """Represents a hash time lock."""
 
     amount: PaymentWithFeeAmount
     expiration: BlockExpiration
@@ -183,7 +201,7 @@ def __post_init__(self) -> None:
 
 @dataclass
 class UnlockPartialProofState(State):
-    """ Stores the lock along with its unlocking secret. """
+    """Stores the lock along with its unlocking secret."""
 
     lock: HashTimeLockState
     secret: Secret = field(repr=False)
@@ -204,7 +222,7 @@ def __post_init__(self) -> None:
 
 @dataclass
 class TransactionExecutionStatus(State):
-    """ Represents the status of a transaction. """
+    """Represents the status of a transaction."""
 
     SUCCESS = "success"
     FAILURE = "failure"
@@ -236,7 +254,7 @@ def __post_init__(self) -> None:
 
 @dataclass
 class SuccessfulTransactionState(State):
-    """ Represents the status of a transaction. """
+    """Represents the status of a transaction."""
 
     finished_block_number: BlockNumber
     started_block_number: Optional[BlockNumber] = None
@@ -280,6 +298,7 @@ class ExpiredWithdrawState:
     total_withdraw: WithdrawAmount
     expiration: BlockExpiration
     nonce: Nonce
+    recipient_metadata: Optional[AddressMetadata] = None
 
 
 @dataclass
@@ -287,11 +306,12 @@ class PendingWithdrawState:
     total_withdraw: WithdrawAmount
     expiration: BlockExpiration
     nonce: Nonce
+    recipient_metadata: Optional[AddressMetadata] = None
 
 
 @dataclass
 class NettingChannelEndState(State):
-    """ The state of one of the nodes in a two party netting channel. """
+    """The state of one of the nodes in a two party netting channel."""
 
     address: Address
     contract_balance: Balance
@@ -352,7 +372,7 @@ def transferred_amount(self) -> TokenAmount:
 
 @dataclass
 class NettingChannelState(State):
-    """ The state of a netting channel. """
+    """The state of a netting channel."""
 
     canonical_identifier: CanonicalIdentifier
     token_address: TokenAddress = field(repr=False)
@@ -444,7 +464,7 @@ def partner_total_withdraw(self) -> WithdrawAmount:
 
 @dataclass
 class TokenNetworkState(State):
-    """ Corresponds to a token network smart contract. """
+    """Corresponds to a token network smart contract."""
 
     address: TokenNetworkAddress
     token_address: TokenAddress
@@ -466,7 +486,7 @@ def __post_init__(self) -> None:
 
 @dataclass(init=False)
 class TokenNetworkRegistryState(State):
-    """ Corresponds to a registry smart contract. """
+    """Corresponds to a registry smart contract."""
 
     class Meta:
         unknown = marshmallow.EXCLUDE
@@ -561,7 +581,7 @@ def __repr__(self) -> str:
     def addresses_to_channel(
         self,
     ) -> Dict[Tuple[TokenNetworkAddress, Address], NettingChannelState]:
-        """ Find the channel for a partner by his address and token network """
+        """Find the channel for a partner by his address and token network"""
         return {
             (token_network.address, channel.partner_state.address): channel
             for token_network_registry in self.identifiers_to_tokennetworkregistries.values()
diff --git a/raiden/transfer/state_change.py b/raiden/transfer/state_change.py
index d7f7a133b6..8d5321bcdb 100644
--- a/raiden/transfer/state_change.py
+++ b/raiden/transfer/state_change.py
@@ -19,6 +19,7 @@
 from raiden.utils.secrethash import sha256_secrethash
 from raiden.utils.typing import (
     Address,
+    AddressMetadata,
     BlockExpiration,
     BlockGasLimit,
     BlockHash,
@@ -29,6 +30,7 @@
     Locksroot,
     MessageID,
     Nonce,
+    Optional,
     PaymentID,
     Secret,
     SecretHash,
@@ -49,7 +51,7 @@
 
 @dataclass(frozen=True)
 class BalanceProofStateChange(AuthenticatedSenderStateChange):
-    """ Marker used for state changes which contain a balance proof. """
+    """Marker used for state changes which contain a balance proof."""
 
     balance_proof: BalanceProofSignedState
 
@@ -84,7 +86,7 @@ class ActionCancelPayment(StateChange):
 
 @dataclass(frozen=True)
 class ActionChannelClose(StateChange):
-    """ User is closing an existing channel. """
+    """User is closing an existing channel."""
 
     canonical_identifier: CanonicalIdentifier
 
@@ -103,10 +105,11 @@ def channel_identifier(self) -> ChannelID:
 
 @dataclass(frozen=True)
 class ActionChannelWithdraw(StateChange):
-    """ Withdraw funds from channel. """
+    """Withdraw funds from channel."""
 
     canonical_identifier: CanonicalIdentifier
     total_withdraw: WithdrawAmount
+    recipient_metadata: Optional[AddressMetadata] = None
 
     @property
     def channel_identifier(self) -> ChannelID:
@@ -115,7 +118,7 @@ def channel_identifier(self) -> ChannelID:
 
 @dataclass(frozen=True)
 class ContractReceiveChannelNew(ContractReceiveStateChange):
-    """ A new channel was created and this node IS a participant. """
+    """A new channel was created and this node IS a participant."""
 
     channel_state: NettingChannelState
 
@@ -130,7 +133,7 @@ def channel_identifier(self) -> ChannelID:
 
 @dataclass(frozen=True)
 class ContractReceiveChannelClosed(ContractReceiveStateChange):
-    """ A channel to which this node IS a participant was closed. """
+    """A channel to which this node IS a participant was closed."""
 
     transaction_from: Address
     canonical_identifier: CanonicalIdentifier
@@ -146,7 +149,7 @@ def token_network_address(self) -> TokenNetworkAddress:
 
 @dataclass(frozen=True)
 class ContractReceiveChannelDeposit(ContractReceiveStateChange):
-    """ A channel to which this node IS a participant had a deposit. """
+    """A channel to which this node IS a participant had a deposit."""
 
     canonical_identifier: CanonicalIdentifier
     deposit_transaction: TransactionChannelDeposit
@@ -163,7 +166,7 @@ def token_network_address(self) -> TokenNetworkAddress:
 
 @dataclass(frozen=True)
 class ContractReceiveChannelWithdraw(ContractReceiveStateChange):
-    """ A channel to which this node IS a participant had a withdraw. """
+    """A channel to which this node IS a participant had a withdraw."""
 
     canonical_identifier: CanonicalIdentifier
     participant: Address
@@ -181,7 +184,7 @@ def token_network_address(self) -> TokenNetworkAddress:
 
 @dataclass(frozen=True)
 class ContractReceiveChannelSettled(ContractReceiveStateChange):
-    """ A channel to which this node IS a participant was settled. """
+    """A channel to which this node IS a participant was settled."""
 
     canonical_identifier: CanonicalIdentifier
     our_onchain_locksroot: Locksroot
@@ -198,7 +201,7 @@ def token_network_address(self) -> TokenNetworkAddress:
 
 @dataclass(frozen=True)
 class ActionChannelSetRevealTimeout(StateChange):
-    """ Change the reveal timeout value of a given channel. """
+    """Change the reveal timeout value of a given channel."""
 
     canonical_identifier: CanonicalIdentifier
     reveal_timeout: BlockTimeout
@@ -227,7 +230,7 @@ def __post_init__(self) -> None:
 
 @dataclass(frozen=True)
 class ContractReceiveNewTokenNetwork(ContractReceiveStateChange):
-    """ A new token was registered with the token network registry. """
+    """A new token was registered with the token network registry."""
 
     token_network_registry_address: TokenNetworkRegistryAddress
     token_network: TokenNetworkState
@@ -239,7 +242,7 @@ def __post_init__(self) -> None:
 
 @dataclass(frozen=True)
 class ContractReceiveSecretReveal(ContractReceiveStateChange):
-    """ A new secret was registered with the SecretRegistry contract. """
+    """A new secret was registered with the SecretRegistry contract."""
 
     secret_registry_address: SecretRegistryAddress
     secrethash: SecretHash
@@ -283,7 +286,7 @@ def token_network_address(self) -> TokenNetworkAddress:
 
 @dataclass(frozen=True)
 class ContractReceiveRouteNew(ContractReceiveStateChange):
-    """ New channel was created and this node is NOT a participant. """
+    """New channel was created and this node is NOT a participant."""
 
     canonical_identifier: CanonicalIdentifier
     participant1: Address
@@ -345,7 +348,7 @@ class ReceiveProcessed(AuthenticatedSenderStateChange):
 
 @dataclass(frozen=True)
 class ReceiveWithdrawRequest(AuthenticatedSenderStateChange):
-    """ A Withdraw message received. """
+    """A Withdraw message received."""
 
     message_identifier: MessageID
     canonical_identifier: CanonicalIdentifier
@@ -354,6 +357,7 @@ class ReceiveWithdrawRequest(AuthenticatedSenderStateChange):
     expiration: BlockExpiration
     signature: Signature
     participant: Address
+    sender_metadata: Optional[AddressMetadata] = None
 
     @property
     def channel_identifier(self) -> ChannelID:
@@ -366,7 +370,7 @@ def token_network_address(self) -> TokenNetworkAddress:
 
 @dataclass(frozen=True)
 class ReceiveWithdrawConfirmation(AuthenticatedSenderStateChange):
-    """ A Withdraw message was received. """
+    """A Withdraw message was received."""
 
     message_identifier: MessageID
     canonical_identifier: CanonicalIdentifier
@@ -387,7 +391,7 @@ def token_network_address(self) -> TokenNetworkAddress:
 
 @dataclass(frozen=True)
 class ReceiveWithdrawExpired(AuthenticatedSenderStateChange):
-    """ A WithdrawExpired message was received. """
+    """A WithdrawExpired message was received."""
 
     message_identifier: MessageID
     canonical_identifier: CanonicalIdentifier
@@ -407,7 +411,7 @@ def token_network_address(self) -> TokenNetworkAddress:
 
 @dataclass(frozen=True)
 class UpdateServicesAddressesStateChange(StateChange):
-    """ A `RegisteredService` contract event was received. """
+    """A `RegisteredService` contract event was received."""
 
     service: Address
     valid_till: int
diff --git a/raiden/transfer/views.py b/raiden/transfer/views.py
index 91bd34fe2a..37d71f71fc 100644
--- a/raiden/transfer/views.py
+++ b/raiden/transfer/views.py
@@ -152,7 +152,7 @@ def get_token_network_address_by_token_address(
 def get_token_network_addresses(
     chain_state: ChainState, token_network_registry_address: TokenNetworkRegistryAddress
 ) -> List[TokenNetworkAddress]:
-    """ Return the list of token networks registered with the given token network registry. """
+    """Return the list of token networks registered with the given token network registry."""
     token_network_registry = chain_state.identifiers_to_tokennetworkregistries.get(
         token_network_registry_address
     )
@@ -167,7 +167,7 @@ def get_token_network_addresses(
 def get_token_identifiers(
     chain_state: ChainState, token_network_registry_address: TokenNetworkRegistryAddress
 ) -> List[TokenAddress]:
-    """ Return the list of tokens registered with the given token network registry. """
+    """Return the list of tokens registered with the given token network registry."""
     token_network_registry = chain_state.identifiers_to_tokennetworkregistries.get(
         token_network_registry_address
     )
@@ -239,7 +239,7 @@ def get_channelstate_for(
     token_address: TokenAddress,
     partner_address: Address,
 ) -> Optional[NettingChannelState]:
-    """ Return the NettingChannelState if it exists, None otherwise. """
+    """Return the NettingChannelState if it exists, None otherwise."""
     token_network = get_token_network_by_token_address(
         chain_state, token_network_registry_address, token_address
     )
@@ -261,7 +261,7 @@ def get_channelstate_for(
 def get_channelstate_by_token_network_and_partner(
     chain_state: ChainState, token_network_address: TokenNetworkAddress, partner_address: Address
 ) -> Optional[NettingChannelState]:
-    """ Return the NettingChannelState if it exists, None otherwise. """
+    """Return the NettingChannelState if it exists, None otherwise."""
     token_network = get_token_network_by_address(chain_state, token_network_address)
 
     channel_state = None
@@ -280,7 +280,7 @@ def get_channelstate_by_token_network_and_partner(
 def get_channelstate_by_canonical_identifier(
     chain_state: ChainState, canonical_identifier: CanonicalIdentifier
 ) -> Optional[NettingChannelState]:
-    """ Return the NettingChannelState if it exists, None otherwise. """
+    """Return the NettingChannelState if it exists, None otherwise."""
     token_network = get_token_network_by_address(
         chain_state, canonical_identifier.token_network_address
     )
@@ -300,7 +300,7 @@ def get_channelstate_filter(
     token_address: TokenAddress,
     filter_fn: Callable,
 ) -> List[NettingChannelState]:
-    """ Return the state of channels that match the condition in `filter_fn` """
+    """Return the state of channels that match the condition in `filter_fn`"""
     token_network = get_token_network_by_token_address(
         chain_state, token_network_registry_address, token_address
     )
diff --git a/raiden/ui/app.py b/raiden/ui/app.py
index ce9dc7ffaa..7f3f2f7393 100644
--- a/raiden/ui/app.py
+++ b/raiden/ui/app.py
@@ -37,6 +37,7 @@
     DEFAULT_HTTP_SERVER_PORT,
     DEFAULT_MATRIX_KNOWN_SERVERS,
     DEFAULT_NUMBER_OF_BLOCK_CONFIRMATIONS,
+    MATRIX_AUTO_SELECT_SERVER,
     MatrixTransportConfig,
     PythonApiConfig,
     RaidenConfig,
@@ -368,9 +369,10 @@ def run_raiden_service(
     else:
         deployed_addresses = load_deployment_addresses_from_contracts(contracts=contracts)
 
-    # Always fetch all available matrix servers. It's necessary to know the complete list in order
-    # to be able to construct user-ids on other homeservers
-    fetch_available_matrix_servers(config.transport, config.environment_type)
+    # Load the available matrix servers when no matrix server is given
+    # The list is used in a PFS check
+    if config.transport.server == MATRIX_AUTO_SELECT_SERVER:
+        fetch_available_matrix_servers(config.transport, config.environment_type)
 
     raiden_bundle = raiden_bundle_from_contracts_deployment(
         proxy_manager=proxy_manager,
diff --git a/raiden/ui/cli.py b/raiden/ui/cli.py
index 155c56854c..99d5e710fe 100644
--- a/raiden/ui/cli.py
+++ b/raiden/ui/cli.py
@@ -151,7 +151,7 @@ def handle_version_option(ctx: Context, _param: Any, value: bool) -> None:
     ),
     option(
         "--datadir",
-        help="Directory for storing raiden data.",
+        help="Directory for storing Raiden data.",
         default="~/.raiden",
         type=ExpandablePath(
             exists=False,
@@ -179,7 +179,7 @@ def handle_version_option(ctx: Context, _param: Any, value: bool) -> None:
     option(
         "--keystore-path",
         help=(
-            "If you have a non-standard path for the ethereum keystore directory"
+            "If you have a non-standard path for the Ethereum keystore directory"
             " provide it using this argument."
         ),
         default=None,
@@ -189,7 +189,7 @@ def handle_version_option(ctx: Context, _param: Any, value: bool) -> None:
     option(
         "--address",
         help=(
-            "The ethereum address you would like raiden to use and for which "
+            "The Ethereum address you would like Raiden to use and for which "
             "a keystore file exists in your local system."
         ),
         default=None,
@@ -278,28 +278,26 @@ def handle_version_option(ctx: Context, _param: Any, value: bool) -> None:
         "Ethereum Node Options",
         option(
             "--sync-check/--no-sync-check",
-            help="Checks if the ethereum node is synchronized against etherscan.",
+            help="Checks if the Ethereum node is synchronized against etherscan.",
             default=True,
             show_default=True,
         ),
         option(
             "--gas-price",
             help=(
-                "Set the gas price for ethereum transactions. If not provided "
-                "the normal gas price strategy is used.\n"
+                "Set the gas price for Ethereum transactions.\n"
                 "Available options:\n"
                 '"fast" - transactions are usually mined within 60 seconds\n'
                 '"normal" - transactions are usually mined within 5 minutes\n'
-                "<GAS_PRICE> - use given gas price\n"
             ),
             type=GasPriceChoiceType(["normal", "fast"]),
             default="fast",
-            show_default=True,
+            show_default="fast",
         ),
         option(
             ETH_RPC_CONFIG_OPTION,
             help=(
-                '"host:port" address of ethereum JSON-RPC server.\n'
+                '"host:port" address of Ethereum JSON-RPC server.\n'
                 "Also accepts a protocol prefix (http:// or https://) with optional port"
             ),
             default="http://127.0.0.1:8545",  # geth default jsonrpc port
@@ -326,7 +324,7 @@ def handle_version_option(ctx: Context, _param: Any, value: bool) -> None:
                 f"URL to the Raiden path finding service to request paths from.\n "
                 f"Example: https://pfs-ropsten.services-dev.raiden.network\n "
                 f"Can also be given the '{MATRIX_AUTO_SELECT_SERVER}' value "
-                f"so that raiden chooses a PFS randomly from the service "
+                f"so that Raiden chooses a PFS randomly from the service "
                 f"registry contract."
             ),
             default=MATRIX_AUTO_SELECT_SERVER,
@@ -546,7 +544,7 @@ def handle_version_option(ctx: Context, _param: Any, value: bool) -> None:
 if find_spec("IPython"):
     OPTIONS.append(
         option(
-            "--console/--no-console", help="Start the interactive raiden console", default=False
+            "--console/--no-console", help="Start the interactive Raiden console", default=False
         )
     )
 else:
@@ -594,10 +592,9 @@ def _run(ctx: Context, **kwargs: Any) -> None:
         )
 
         if kwargs["config_file"] is not None:
-            source = ctx.get_parameter_source("config_file")  # type: ignore
-            log.debug(
-                "Using config file", config_file=kwargs["config_file"], set_by=source.name.title()
-            )
+            source = ctx.get_parameter_source("config_file")
+            set_by = source.name.title() if source else None
+            log.debug("Using config file", config_file=kwargs["config_file"], set_by=set_by)
 
         enable_gevent_monitoring_signal()
 
@@ -815,7 +812,7 @@ def _run(ctx: Context, **kwargs: Any) -> None:
 @option("--short", is_flag=True, help="Only display Raiden version")
 @click.pass_context
 def version(ctx: Context, short: bool) -> None:
-    """Print version information and exit. """
+    """Print version information and exit."""
     click.echo(get_version(short=short))
     ctx.exit(0)
 
@@ -844,7 +841,7 @@ def smoketest(
 def _smoketest(
     ctx: Context, debug: bool, eth_client: EthClient, report_path: Optional[str]
 ) -> None:  # pragma: no cover
-    """ Test, that the raiden installation is sane. """
+    """Test, that the Raiden installation is sane."""
     from raiden.tests.utils.smoketest import run_smoketest, setup_smoketest, step_printer
 
     raiden_stdout = StringIO()
diff --git a/raiden/ui/console.py b/raiden/ui/console.py
index 0389d0c41e..395c0419e4 100644
--- a/raiden/ui/console.py
+++ b/raiden/ui/console.py
@@ -92,7 +92,7 @@ def lastlog(n: int = 10, prefix: str = None, level: str = None) -> None:
                 print(line)
 
         def lasterr(n: int = 1) -> None:
-            """ Print the last `n` entries of stderr to stdout. """
+            """Print the last `n` entries of stderr to stdout."""
             for line in (err.getvalue().strip().split("\n") or [])[-n:]:
                 print(line)
 
@@ -119,7 +119,7 @@ def lasterr(n: int = 1) -> None:
 
 
 class ConsoleTools:
-    """ Some functions to make working in the console easier. """
+    """Some functions to make working in the console easier."""
 
     def __init__(self, raiden_service: RaidenService) -> None:
         self._raiden = raiden_service
diff --git a/raiden/ui/prompt.py b/raiden/ui/prompt.py
index 1853d2d0da..ec8d4e352d 100644
--- a/raiden/ui/prompt.py
+++ b/raiden/ui/prompt.py
@@ -22,7 +22,7 @@ def prompt_account(account_manager: AccountManager) -> AddressHex:
     print("")
 
     while True:
-        idx = click.prompt("Select one of them by index to continue", type=int)
+        idx = click.prompt("Select one of them by index to continue", type=int)  # type: ignore
 
         if 0 <= idx < len(addresses):
             return addresses[idx]
diff --git a/raiden/ui/sync.py b/raiden/ui/sync.py
index e2450e6e03..9b35dc1663 100644
--- a/raiden/ui/sync.py
+++ b/raiden/ui/sync.py
@@ -15,7 +15,7 @@
 
 
 def blockcypher_query_with_retries(sleep: float, retries: int = 3) -> Optional[BlockNumber]:
-    """ Queries blockcypher for latest mainnet block number """
+    """Queries blockcypher for latest mainnet block number"""
 
     def make_request() -> BlockNumber:
         response = requests.get(
diff --git a/raiden/utils/cli.py b/raiden/utils/cli.py
index 4ae58ad118..d3cf5b7868 100644
--- a/raiden/utils/cli.py
+++ b/raiden/utils/cli.py
@@ -15,7 +15,7 @@
 import structlog
 from click import Choice
 from click._compat import term_len
-from click.core import ParameterSource, augment_usage_errors  # type: ignore
+from click.core import ParameterSource, augment_usage_errors
 from click.formatting import iter_rows, measure_table, wrap_text
 from toml import TomlDecodeError, load
 from web3.gas_strategies.time_based import fast_gas_price_strategy
@@ -105,7 +105,7 @@ def __init__(
         option_parser_priority=None,
         **attrs,
     ):
-        super().__init__(  # type: ignore
+        super().__init__(
             param_decls,
             show_default,
             prompt,
@@ -219,7 +219,7 @@ def invoke(self, ctx):
                                 param = self.opt_name_to_param[param_name]
                                 with augment_usage_errors(ctx, param=param):
                                     try:
-                                        parsed_value = param.full_process_value(ctx, parser_value)
+                                        parsed_value = param.process_value(ctx, parser_value)
                                         if param.callback is not None:
                                             value = param.callback(ctx, param, parsed_value)
                                     except Exception:
@@ -253,11 +253,11 @@ def command(name=None, cls=GroupableOptionCommand, **attrs):
 
 
 def group(name=None, **attrs):
-    return click.group(name, **{"cls": GroupableOptionCommandGroup, **attrs})  # type: ignore
+    return click.group(name, **{"cls": GroupableOptionCommandGroup, **attrs})
 
 
 def option(*args, **kwargs):
-    return click.option(*args, **{"cls": GroupableOption, **kwargs})  # type: ignore
+    return click.option(*args, **{"cls": GroupableOption, **kwargs})
 
 
 def option_group(name: str, *options: Callable):
@@ -352,7 +352,7 @@ def convert(self, value, param, ctx):  # pylint: disable=inconsistent-return-sta
 
 
 class GasPriceChoiceType(click.Choice):
-    """ Returns a GasPriceStrategy for the choice """
+    """Returns a GasPriceStrategy for the choice"""
 
     def convert(self, value, param, ctx):  # pylint: disable=inconsistent-return-statements
         if isinstance(value, str) and value.isnumeric():
@@ -438,8 +438,8 @@ class Parser(metaclass=ABCMeta):
     def __init__(self, param_name: str, priority: int = None):
         self.name = param_name
         self.priority = priority or self.default_priority
-        self.name_map: MutableMapping[str, str] = dict()
-        self._internal_names: Set[str] = set()
+        self.name_map: MutableMapping[str, Optional[str]] = dict()
+        self._internal_names: Set[Optional[str]] = set()
 
     def __lt__(self, other: object) -> bool:
         if not isinstance(other, Parser):
diff --git a/raiden/utils/datastructures.py b/raiden/utils/datastructures.py
index 9b81500a95..1c7a1d2be0 100644
--- a/raiden/utils/datastructures.py
+++ b/raiden/utils/datastructures.py
@@ -4,7 +4,7 @@
 
 
 def merge_dict(to_update: dict, other_dict: dict) -> None:
-    """ merges b into a """
+    """merges b into a"""
     for key, value in other_dict.items():
         has_map = isinstance(value, collections.Mapping) and isinstance(
             to_update.get(key, None), collections.Mapping
@@ -17,7 +17,7 @@ def merge_dict(to_update: dict, other_dict: dict) -> None:
 
 
 def split_in_pairs(arg: Iterable) -> Iterable[Tuple]:
-    """ Split given iterable in pairs [a, b, c, d, e] -> [(a, b), (c, d), (e, None)]"""
+    """Split given iterable in pairs [a, b, c, d, e] -> [(a, b), (c, d), (e, None)]"""
     # We are using zip_longest with one clever hack:
     # https://docs.python.org/3/library/itertools.html#itertools.zip_longest
     # We create an iterator out of the list and then pass the same iterator to
diff --git a/raiden/utils/debugging.py b/raiden/utils/debugging.py
index 1a557d766d..cfdc084265 100644
--- a/raiden/utils/debugging.py
+++ b/raiden/utils/debugging.py
@@ -96,7 +96,7 @@ class IdleMeasurement:
 
 @dataclass
 class Idle:
-    """ Measures how much time the thread waited on the libev backend. """
+    """Measures how much time the thread waited on the libev backend."""
 
     measurement_interval: float
     before_poll: Optional[float] = None
@@ -172,17 +172,17 @@ def running_interval(self) -> float:
 
     @property
     def idled(self) -> float:
-        """ The amount of seconds the thread idled. """
+        """The amount of seconds the thread idled."""
         return sum(interval.after_poll - interval.before_poll for interval in self.measurements)
 
     @property
     def idled_pct(self) -> float:
-        """ The percentage of time the thread idled, waiting on the event loop. """
+        """The percentage of time the thread idled, waiting on the event loop."""
         return self.idled / self.running_interval
 
     @property
     def context_switches(self) -> int:
-        """ The number of context switches done for the past `measurement_interval`. """
+        """The number of context switches done for the past `measurement_interval`."""
         return len(IDLE.measurements)
 
     def log(self) -> None:
diff --git a/raiden/utils/formatting.py b/raiden/utils/formatting.py
index 18586bf6b0..15fb177df5 100644
--- a/raiden/utils/formatting.py
+++ b/raiden/utils/formatting.py
@@ -82,7 +82,7 @@ def to_hex_address(address: AddressTypes) -> AddressHex:
 
 
 def format_block_id(block_id: BlockIdentifier) -> str:
-    """ Formats a block identifier to a string. """
+    """Formats a block identifier to a string."""
     # Got a block hash
     if isinstance(block_id, T_BlockHash):
         return encode_hex(block_id)
diff --git a/raiden/utils/gevent.py b/raiden/utils/gevent.py
index fc6eafb09e..31af5f6670 100644
--- a/raiden/utils/gevent.py
+++ b/raiden/utils/gevent.py
@@ -4,7 +4,7 @@
 
 
 def spawn_named(name: str, task: Callable, *args: Any, **kwargs: Any) -> Greenlet:
-    """ Helper function to spawn a greenlet with a name. """
+    """Helper function to spawn a greenlet with a name."""
 
     greenlet = Greenlet(task, *args, **kwargs)
     greenlet.name = name
diff --git a/raiden/utils/http.py b/raiden/utils/http.py
index dcb02503e3..2d830b08f9 100644
--- a/raiden/utils/http.py
+++ b/raiden/utils/http.py
@@ -51,7 +51,7 @@ def send(self, request, **kwargs):
 
 
 class HTTPExecutor(MiHTTPExecutor):  # pragma: no cover
-    """ Subclass off mirakuru.HTTPExecutor, which allows other methods than HEAD """
+    """Subclass off mirakuru.HTTPExecutor, which allows other methods than HEAD"""
 
     def __init__(
         self,
@@ -71,7 +71,7 @@ def __init__(
         self.verify_tls = verify_tls
 
     def after_start_check(self):
-        """ Check if defined URL returns expected status to a <method> request. """
+        """Check if defined URL returns expected status to a <method> request."""
         try:
             if self.url.scheme == "http":
                 conn = HTTPConnection(self.host, self.port)
@@ -197,7 +197,7 @@ def wait_for(self, wait_for):
         raise TimeoutExpired(self, timeout=self._timeout)
 
     def running(self) -> bool:
-        """ Include pre_start_check in running, so stop will wait for the underlying listener """
+        """Include pre_start_check in running, so stop will wait for the underlying listener"""
         return super().running() or self.pre_start_check()
 
     def _send_request(self, conn: HTTPConnection):
diff --git a/raiden/utils/keys.py b/raiden/utils/keys.py
index 2cc0d647aa..4baf9d32bd 100644
--- a/raiden/utils/keys.py
+++ b/raiden/utils/keys.py
@@ -5,7 +5,7 @@
 
 
 def privatekey_to_publickey(private_key_bin: bytes) -> PublicKey:
-    """ Returns public key in bitcoins 'bin' encoding. """
+    """Returns public key in bitcoins 'bin' encoding."""
     if not ishash(private_key_bin):
         raise ValueError("private_key_bin format mismatch. maybe hex encoded?")
     return keys.PrivateKey(private_key_bin).public_key.to_bytes()
diff --git a/raiden/utils/logging.py b/raiden/utils/logging.py
index d1db9aa4cc..0972bde4c0 100644
--- a/raiden/utils/logging.py
+++ b/raiden/utils/logging.py
@@ -2,7 +2,7 @@
 
 
 def redact_secret(data: Dict) -> Dict:
-    """ Modify `data` in-place and replace keys named `secret`. """
+    """Modify `data` in-place and replace keys named `secret`."""
     if not isinstance(data, dict):
         raise ValueError("data must be a dict.")
 
diff --git a/raiden/utils/notifying_queue.py b/raiden/utils/notifying_queue.py
index bbaa593d7b..d1b8db5c13 100644
--- a/raiden/utils/notifying_queue.py
+++ b/raiden/utils/notifying_queue.py
@@ -19,12 +19,12 @@ def __init__(self, maxsize: int = None, items: Iterable[T] = ()) -> None:
             self.set()
 
     def put(self, item: T) -> None:
-        """ Add new item to the queue. """
+        """Add new item to the queue."""
         self.queue.put(item)
         self.set()
 
     def get(self, block: bool = True, timeout: float = None) -> T:
-        """ Removes and returns an item from the queue. """
+        """Removes and returns an item from the queue."""
         value = self.queue.get(block, timeout)
         if self.queue.empty():
             self.clear()
@@ -37,7 +37,7 @@ def __len__(self) -> int:
         return len(self.queue)
 
     def copy(self) -> List[T]:
-        """ Copies the current queue items. """
+        """Copies the current queue items."""
         copy = self.queue.copy()
 
         result = list()
diff --git a/raiden/utils/nursery.py b/raiden/utils/nursery.py
index afab4a6f23..b909e6c50a 100644
--- a/raiden/utils/nursery.py
+++ b/raiden/utils/nursery.py
@@ -156,7 +156,7 @@ def spawn_under_watch(function: Callable, *args: Any, **kwargs: Any) -> Greenlet
                 # `throw` does not raise the exception if the greenlet has
                 # finished, which is exactly the semantics needed here.
                 def stop_greenlet_from_hub(result: AsyncResult) -> None:
-                    """ Stop the greenlet if the nursery is stopped. """
+                    """Stop the greenlet if the nursery is stopped."""
                     try:
                         result.get()
                     except BaseException as e:
@@ -165,7 +165,7 @@ def stop_greenlet_from_hub(result: AsyncResult) -> None:
                         greenlet.throw(GreenletExit())
 
                 def propagate_error(g: Greenlet) -> None:
-                    """ If the greenlet fails, stop the nursery. """
+                    """If the greenlet fails, stop the nursery."""
                     janitor._stop.set_exception(g.exception)
 
                 greenlet.link_exception(propagate_error)
diff --git a/raiden/utils/profiling/profiler.py b/raiden/utils/profiling/profiler.py
index ce24be66d7..258fca8635 100644
--- a/raiden/utils/profiling/profiler.py
+++ b/raiden/utils/profiling/profiler.py
@@ -66,7 +66,7 @@ def _copy_call(call):
 
 
 def ensure_call(curr, call):
-    """ Returns an existing entry of call or create a new one. """
+    """Returns an existing entry of call or create a new one."""
     id_ = call["runtime_id"]
 
     if id_ not in curr.children:
@@ -144,7 +144,7 @@ def calculate_metrics(info):
 
 
 class GlobalState(dict):
-    """ This class is responsable to store the state of a profiling session """
+    """This class is responsable to store the state of a profiling session"""
 
     def __init__(self, *args, **kwargs):
         super().__init__(*args, **kwargs)
@@ -175,7 +175,7 @@ def __init__(self, name, trace):
 
     @property
     def total_accumulated(self):
-        """ Returns the top most stack that has a wall time measurement """
+        """Returns the top most stack that has a wall time measurement"""
 
         accumulated, top_depth = 0.0, None
         for depth, node in self.depthorder():
@@ -228,7 +228,7 @@ def switch_exit(self, now):
             info["sleep_start_time"].append(None)
 
     def depthorder(self):
-        """ Returns a generator that does stack traversal stepping in the depth order. """
+        """Returns a generator that does stack traversal stepping in the depth order."""
         iterators = [
             (1, self.root.children[key])
             for key in self.root.children  # the root node doesnt have data
@@ -249,7 +249,7 @@ def depthorder(self):
                 iterators.extend(children)
 
     def traverse(self):
-        """ Returns a generator that does stack travesal using order of appearance (inorder). """
+        """Returns a generator that does stack travesal using order of appearance (inorder)."""
         iterators = [(1, self.root.children[key]) for key in self.root.children]
 
         while iterators:
diff --git a/raiden/utils/profiling/sampler.py b/raiden/utils/profiling/sampler.py
index 4e42830172..91ca801b2c 100644
--- a/raiden/utils/profiling/sampler.py
+++ b/raiden/utils/profiling/sampler.py
@@ -208,7 +208,7 @@ def stop(self) -> None:
 
 
 class SignalSampler:
-    """ Signal based sampler. """
+    """Signal based sampler."""
 
     def __init__(
         self,
diff --git a/raiden/utils/profiling/trace.py b/raiden/utils/profiling/trace.py
index 0c425d3280..59e3dae427 100644
--- a/raiden/utils/profiling/trace.py
+++ b/raiden/utils/profiling/trace.py
@@ -33,7 +33,7 @@ def __init__(self, datadir: str) -> None:
         self.timer = Timer(self._trace, interval=MINUTE * 5)
 
     def _trace(self, signum: int, frame: FrameType) -> None:  # pylint: disable=unused-argument
-        """ Signal handler used to take snapshots of the running process. """
+        """Signal handler used to take snapshots of the running process."""
 
         # the last pending signal after trace_stop
         if not self.profiling:
diff --git a/raiden/utils/runnable.py b/raiden/utils/runnable.py
index 62d6fcd1ee..bbfeca0ac5 100644
--- a/raiden/utils/runnable.py
+++ b/raiden/utils/runnable.py
@@ -80,7 +80,7 @@ def on_error(self, subtask: Greenlet) -> None:
     def _schedule_new_greenlet(
         self, func: Callable, *args: Any, in_seconds_from_now: int = None, **kwargs: Any
     ) -> Greenlet:
-        """ Spawn a sub-task and ensures an error on it crashes self/main greenlet """
+        """Spawn a sub-task and ensures an error on it crashes self/main greenlet"""
 
         def on_success(greenlet: Greenlet) -> None:
             if greenlet in self.greenlets:
diff --git a/raiden/utils/signer.py b/raiden/utils/signer.py
index 4181248e74..684ac77627 100644
--- a/raiden/utils/signer.py
+++ b/raiden/utils/signer.py
@@ -24,7 +24,7 @@ def eth_sign_sha3(data: bytes) -> bytes:
 def recover(
     data: bytes, signature: Signature, hasher: Callable[[bytes], bytes] = eth_sign_sha3
 ) -> Address:
-    """ eth_recover address from data hash and signature """
+    """eth_recover address from data hash and signature"""
     _hash = hasher(data)
 
     # ecdsa_recover accepts only standard [0,1] v's so we add support also for [27,28] here
@@ -41,14 +41,14 @@ def recover(
 
 
 class Signer(ABC):
-    """ ABC for Signer interface """
+    """ABC for Signer interface"""
 
     # attribute or cached property which represents the address of the account of this Signer
     address: Address
 
     @abstractmethod
     def sign(self, data: bytes, v: int = 27) -> Signature:
-        """ Sign data hash (as of EIP191) with this Signer's account """
+        """Sign data hash (as of EIP191) with this Signer's account"""
         pass
 
     # TODO: signTransaction (replace privkey on JSONRPCClient)
@@ -73,7 +73,7 @@ def __repr__(self) -> str:
 
 
 class LocalSigner(Signer):
-    """ Concrete Signer implementation using a local private key """
+    """Concrete Signer implementation using a local private key"""
 
     private_key: keys.PrivateKey
 
@@ -82,7 +82,7 @@ def __init__(self, private_key: bytes) -> None:
         self.address = self.private_key.public_key.to_canonical_address()
 
     def sign(self, data: bytes, v: int = 27) -> Signature:
-        """ Sign data hash with local private key """
+        """Sign data hash with local private key"""
         assert v in (0, 27), "Raiden is only signing messages with v in (0, 27)"
         _hash = eth_sign_sha3(data)
         signature = self.private_key.sign_msg_hash(message_hash=_hash)
diff --git a/raiden/utils/transfers.py b/raiden/utils/transfers.py
index 2ed50113f3..fd25439a18 100644
--- a/raiden/utils/transfers.py
+++ b/raiden/utils/transfers.py
@@ -6,15 +6,15 @@
 
 
 def random_secret() -> Secret:
-    """ Return a random 32 byte secret"""
+    """Return a random 32 byte secret"""
     return Secret(os.urandom(constants.SECRET_LENGTH))
 
 
 def create_default_identifier() -> PaymentID:
-    """ Generates a random identifier. """
+    """Generates a random identifier."""
     return PaymentID(random.randint(0, constants.UINT64_MAX))
 
 
 def to_rdn(rei: int) -> float:
-    """ Convert REI value to RDN. """
+    """Convert REI value to RDN."""
     return rei / 10 ** 18
diff --git a/raiden/utils/typing.py b/raiden/utils/typing.py
index 80febdc1f5..d6f5c3b91e 100644
--- a/raiden/utils/typing.py
+++ b/raiden/utils/typing.py
@@ -204,7 +204,7 @@ def typecheck(value: Any, expected: Union[Type, Tuple[Type, ...]]) -> None:
 T_UserID = str
 UserID = NewType("UserID", T_UserID)
 
-AddressMetadata = Dict[str, Union[UserID, PeerCapabilities]]
+AddressMetadata = Dict[str, Union[UserID, str]]
 
 AddressTypes = Union[
     Address,
diff --git a/raiden/utils/upgrades.py b/raiden/utils/upgrades.py
index d1289ec250..df3ed6948f 100644
--- a/raiden/utils/upgrades.py
+++ b/raiden/utils/upgrades.py
@@ -1,5 +1,6 @@
 import os
 import sqlite3
+import sys
 from contextlib import closing
 from glob import escape, glob
 from pathlib import Path
@@ -104,7 +105,7 @@ def delete_dbs_with_failed_migrations(valid_db_names: List[Path]) -> None:
 
 
 class UpgradeManager:
-    """Run migrations when a database upgrade is necesary.
+    """Run migrations when a database upgrade is necessary.
 
     Skip the upgrade if either:
 
@@ -166,6 +167,22 @@ def run(self) -> None:
                 f"older."
             )
 
+        if RAIDEN_DB_VERSION >= 27 and file_version <= 26 and file_version > 1:
+            msg = (
+                f"Your Raiden database is version {file_version} and there is no compatible "
+                f"migration to version {RAIDEN_DB_VERSION} available.\n"
+                "You need to either start a new Raiden node with a different account, or "
+                "close and settle all channels, and start over with a fresh database.\n\n"
+                "More information on this topic at "
+                "https://raiden-network.readthedocs.io/en/latest/other/known-issues.html"
+                "#database-upgrades\n\n"
+                "If you are on **mainnet** and affected by this, please create an issue at "
+                "https://github.com/raiden-network/raiden/issues/new?title=Mainnet%20Migration%20"
+                f"{file_version}%20{RAIDEN_DB_VERSION}"
+            )
+            log.warning(msg)
+            sys.exit(msg)
+
         self._upgrade(
             target_file=self._current_db_filename,
             from_file=latest_db_path,
diff --git a/raiden/utils/validation.py b/raiden/utils/validation.py
new file mode 100644
index 0000000000..ffc719be7d
--- /dev/null
+++ b/raiden/utils/validation.py
@@ -0,0 +1,86 @@
+from __future__ import annotations
+
+from enum import Flag, auto
+from typing import TYPE_CHECKING, Dict, Optional, Union
+
+from eth_typing import Address
+
+from raiden.utils.typing import AddressMetadata, UserID
+
+if TYPE_CHECKING:
+    from raiden.messages.metadata import RouteMetadata
+    from raiden.transfer.state import RouteState
+
+
+class MetadataValidationError(Flag):
+    USER_ID_MISSING = auto()
+    DISPLAY_NAME_MISSING = auto()
+    INVALID_SIGNATURE = auto()
+
+
+def validate_address_metadata(
+    instance: Union[RouteMetadata, RouteState]
+) -> Dict[Address, MetadataValidationError]:
+    """Validate address metadata
+
+    This function accepts both ``RouteMetadata`` and ``RouteState`` instances
+    and validates their ``address_metadata`` or ``address_to_metadata`` fields
+    respectively.
+
+    The rules are:
+
+      - Entirely missing metadata (``None`` or empty ``dict``) are ok
+      - If metadata are present both ``user_id`` and ``displayname`` need to be set
+      - ``displayname`` needs to be a valid signature for ``user_id``
+
+    Returns a dictionary of address to validation error flags.
+    """
+
+    # Yay, circular imports
+    from raiden.messages.metadata import RouteMetadata
+    from raiden.network.transport.matrix.utils import validate_user_id_signature
+    from raiden.transfer.state import RouteState
+
+    errors: Dict[Address, MetadataValidationError] = {}
+
+    if isinstance(instance, RouteMetadata):
+        metadata = instance.address_metadata
+    elif isinstance(instance, RouteState):
+        metadata = instance.address_to_metadata
+    else:
+        raise TypeError(f"Received unexpected instance of type {type(instance)}.")
+
+    if not metadata:
+        # Empty metadata are not a failure case
+        return errors
+
+    for address in instance.route:
+        # Special case for `Flag` instances. There's always a `0` value that represents the absence
+        # of a Flag
+        error = MetadataValidationError(0)
+
+        address_metadata: Optional[AddressMetadata] = metadata.get(address)
+        if not address_metadata:
+            # Missing or empty metadata is currently not a failure case
+            continue
+
+        user_id = address_metadata.get("user_id")
+        displayname = address_metadata.get("displayname")
+
+        if user_id is None:
+            error |= MetadataValidationError.USER_ID_MISSING
+        if displayname is None:
+            error |= MetadataValidationError.DISPLAY_NAME_MISSING
+
+        if not error:
+            verified_address = validate_user_id_signature(
+                UserID(user_id),  # type: ignore
+                displayname,
+            )
+            if verified_address != address:
+                error |= MetadataValidationError.INVALID_SIGNATURE
+
+        if error:
+            errors[address] = error
+
+    return errors
diff --git a/raiden/waiting.py b/raiden/waiting.py
index ee99e9a518..df1d278a24 100644
--- a/raiden/waiting.py
+++ b/raiden/waiting.py
@@ -11,12 +11,7 @@
 from raiden.transfer.identifiers import CanonicalIdentifier
 from raiden.transfer.mediated_transfer.events import EventUnlockClaimFailed
 from raiden.transfer.mediated_transfer.state_change import ActionInitMediator, ActionInitTarget
-from raiden.transfer.state import (
-    CHANNEL_AFTER_CLOSE_STATES,
-    ChannelState,
-    NettingChannelEndState,
-    NetworkState,
-)
+from raiden.transfer.state import CHANNEL_AFTER_CLOSE_STATES, ChannelState, NettingChannelEndState
 from raiden.transfer.state_change import (
     ContractReceiveChannelWithdraw,
     ContractReceiveSecretReveal,
@@ -194,7 +189,7 @@ def wait_single_channel_deposit(
     total_deposit: TokenAmount,
     retry_timeout: float,
 ) -> None:
-    """ Wait until a deposit of `total_deposit` for app_deposit is seen by both apps"""
+    """Wait until a deposit of `total_deposit` for app_deposit is seen by both apps"""
     wait_for_participant_deposit(
         raiden=app_deposit,
         token_network_registry_address=registry_address,
@@ -223,7 +218,7 @@ def wait_both_channel_deposit(
     total_deposit: TokenAmount,
     retry_timeout: float,
 ) -> None:
-    """ Wait until a deposit of `total_deposit` for both apps is seen by both apps"""
+    """Wait until a deposit of `total_deposit` for both apps is seen by both apps"""
     wait_single_channel_deposit(
         app_deposit=app_deposit,
         app_partner=app_partner,
@@ -443,46 +438,6 @@ def wait_for_settle(
     )
 
 
-def wait_for_network_state(
-    raiden: "RaidenService",
-    node_address: Address,
-    network_state: NetworkState,
-    retry_timeout: float,
-) -> None:  # pragma: no unittest
-    """Wait until `node_address` becomes healthy.
-
-    Note:
-        This does not time out, use gevent.Timeout.
-    """
-    network_statuses = views.get_networkstatuses(views.state_from_raiden(raiden))
-    current = network_statuses.get(node_address)
-
-    log_details = {
-        "node_address": to_checksum_address(node_address),
-        "target_network_state": network_state,
-    }
-    while current != network_state:
-        assert raiden, TRANSPORT_ERROR_MSG
-        assert raiden.transport, TRANSPORT_ERROR_MSG
-
-        log.debug("wait_for_network_state", current_network_state=current, **log_details)
-        gevent.sleep(retry_timeout)
-        network_statuses = views.get_networkstatuses(views.state_from_raiden(raiden))
-        current = network_statuses.get(node_address)
-
-
-def wait_for_healthy(
-    raiden: "RaidenService", node_address: Address, retry_timeout: float
-) -> None:  # pragma: no unittest
-    """Wait until `node_address` becomes healthy.
-
-    Note:
-        This does not time out, use gevent.Timeout.
-    """
-
-    wait_for_network_state(raiden, node_address, NetworkState.REACHABLE, retry_timeout)
-
-
 class TransferWaitResult(Enum):
     SECRET_REGISTERED_ONCHAIN = "secret registered onchain"
     UNLOCKED = "unlocked"
diff --git a/requirements/requirements-ci.txt b/requirements/requirements-ci.txt
index e6a8f4b441..a9cc1fd973 100644
--- a/requirements/requirements-ci.txt
+++ b/requirements/requirements-ci.txt
@@ -8,6 +8,8 @@ aioice==0.7.5
     # via
     #   -r requirements-dev.txt
     #   aiortc
+aiortc-pyav-stub==0.1.1
+    # via -r requirements-dev.txt
 aiortc==1.2.0
     # via -r requirements-dev.txt
 altgraph==0.16.1
@@ -30,7 +32,7 @@ asn1crypto==1.3.0
     # via
     #   -r requirements-dev.txt
     #   coincurve
-astroid==2.5.2
+astroid==2.5.6
     # via
     #   -r requirements-dev.txt
     #   pylint
@@ -74,7 +76,7 @@ bitarray==1.2.1
     # via
     #   -r requirements-dev.txt
     #   eth-account
-black==20.8b1
+black==21.4b2
     # via -r requirements-dev.txt
 bleach==3.3.0
     # via
@@ -83,7 +85,7 @@ bleach==3.3.0
     #   readme-renderer
 bump2version==1.0.1
     # via -r requirements-dev.txt
-cachetools==4.2.1
+cachetools==4.2.2
     # via -r requirements-dev.txt
 canonicaljson==1.4.0
     # via
@@ -107,7 +109,7 @@ chardet==3.0.4
     # via
     #   -r requirements-dev.txt
     #   requests
-click==8.0.0a1
+click==8.0.1
     # via
     #   -r requirements-dev.txt
     #   black
@@ -132,10 +134,11 @@ crc32c==2.2
     # via
     #   -r requirements-dev.txt
     #   aiortc
-cryptography==3.3.2
+cryptography==3.4.7
     # via
     #   -r requirements-dev.txt
     #   aiortc
+    #   matrix-synapse
     #   pyopenssl
     #   service-identity
 cytoolz==0.10.1
@@ -209,7 +212,7 @@ execnet==1.6.0
     # via
     #   -r requirements-dev.txt
     #   pytest-xdist
-faker==8.1.0
+faker==8.4.0
     # via -r requirements-dev.txt
 fancycompleter==0.8
     # via
@@ -219,7 +222,7 @@ filelock==3.0.12
     # via -r requirements-dev.txt
 flake8-bugbear==21.4.3
     # via -r requirements-dev.txt
-flake8-comprehensions==3.4.0
+flake8-comprehensions==3.5.0
     # via -r requirements-dev.txt
 flake8-tuple==0.4.1
     # via -r requirements-dev.txt
@@ -233,9 +236,9 @@ flaky==3.7.0
     # via -r requirements-dev.txt
 flask-cors==3.0.10
     # via -r requirements-dev.txt
-flask-restful==0.3.8
+flask-restful==0.3.9
     # via -r requirements-dev.txt
-flask==1.1.2
+flask==2.0.1
     # via
     #   -r requirements-dev.txt
     #   flask-cors
@@ -272,7 +275,7 @@ hyperlink==21.0.0
     #   -r requirements-dev.txt
     #   treq
     #   twisted
-hypothesis==6.10.0
+hypothesis==6.13.1
     # via -r requirements-dev.txt
 idna==2.8
     # via
@@ -281,7 +284,7 @@ idna==2.8
     #   matrix-synapse
     #   requests
     #   twisted
-importlib-metadata==3.7.3
+importlib-metadata==4.0.1
     # via
     #   -r requirements-dev.txt
     #   pluggy
@@ -302,13 +305,13 @@ ipython-genutils==0.2.0
     # via
     #   -r requirements-dev.txt
     #   traitlets
-ipython==7.22.0
+ipython==7.24.1
     # via -r requirements-dev.txt
 isort==5.8.0
     # via
     #   -r requirements-dev.txt
     #   pylint
-itsdangerous==1.1.0
+itsdangerous==2.0.1
     # via
     #   -r requirements-dev.txt
     #   flask
@@ -316,7 +319,7 @@ jedi==0.17.0
     # via
     #   -r requirements-dev.txt
     #   ipython
-jinja2==2.11.3
+jinja2==3.0.1
     # via
     #   -r requirements-dev.txt
     #   flask
@@ -336,7 +339,7 @@ lru-dict==1.1.6
     #   web3
 macholib==1.14
     # via -r requirements-ci.in
-markupsafe==1.1.1
+markupsafe==2.0.1
     # via
     #   -r requirements-dev.txt
     #   jinja2
@@ -352,9 +355,13 @@ marshmallow==3.11.1
     #   marshmallow-dataclass
     #   marshmallow-enum
     #   marshmallow-polyfield
+matplotlib-inline==0.1.2
+    # via
+    #   -r requirements-dev.txt
+    #   ipython
 matrix-client==0.3.2
     # via -r requirements-dev.txt
-matrix-synapse==1.29.0
+matrix-synapse==1.33.0
     # via -r requirements-dev.txt
 mccabe==0.6.1
     # via
@@ -406,7 +413,7 @@ parso==0.7.0
     # via
     #   -r requirements-dev.txt
     #   jedi
-pathspec==0.8.0
+pathspec==0.8.1
     # via
     #   -r requirements-dev.txt
     #   black
@@ -462,7 +469,7 @@ py-solc-x==1.1.0
     # via
     #   -r requirements-dev.txt
     #   raiden-contracts
-py-spy==0.3.5
+py-spy==0.3.7
     # via -r requirements-dev.txt
 py==1.10.0
     # via
@@ -514,7 +521,7 @@ pylibsrtp==0.6.8
     # via
     #   -r requirements-dev.txt
     #   aiortc
-pylint==2.7.4
+pylint==2.8.2
     # via -r requirements-dev.txt
 pymacaroons==0.13.0
     # via
@@ -551,7 +558,7 @@ pytest-select==0.1.2
     # via -r requirements-dev.txt
 pytest-xdist==2.2.1
     # via -r requirements-dev.txt
-pytest==6.2.3
+pytest==6.2.4
     # via
     #   -r requirements-dev.txt
     #   pytest-forked
@@ -577,7 +584,7 @@ raiden-api-client==1.1.0
     # via -r requirements-dev.txt
 raiden-contracts==0.37.5
     # via -r requirements-dev.txt
-raiden-webui==1.1.1
+raiden-webui==1.2.1
     # via -r requirements-dev.txt
 readme-renderer==29.0
     # via -r requirements-dev.txt
@@ -624,14 +631,13 @@ simplejson==3.17.2
     # via
     #   -r requirements-dev.txt
     #   canonicaljson
-six==1.15.0
+six==1.16.0
     # via
     #   -r requirements-dev.txt
     #   attrdict
     #   automat
     #   bcrypt
     #   bleach
-    #   cryptography
     #   flake8-tuple
     #   flask-cors
     #   flask-restful
@@ -675,6 +681,7 @@ traitlets==4.3.2
     # via
     #   -r requirements-dev.txt
     #   ipython
+    #   matplotlib-inline
 treq==21.1.0
     # via
     #   -r requirements-dev.txt
@@ -684,19 +691,17 @@ twisted[tls]==21.2.0
     #   -r requirements-dev.txt
     #   matrix-synapse
     #   treq
-typed-ast==1.4.2
+typed-ast==1.4.3
     # via
     #   -r requirements-dev.txt
-    #   black
     #   mypy
 typeguard==2.11.1
     # via
     #   -r requirements-dev.txt
     #   marshmallow-dataclass
-typing-extensions==3.7.4.3
+typing-extensions==3.10.0.0
     # via
     #   -r requirements-dev.txt
-    #   black
     #   matrix-synapse
     #   mypy
     #   signedjson
@@ -711,7 +716,7 @@ unpaddedbase64==1.1.0
     #   -r requirements-dev.txt
     #   matrix-synapse
     #   signedjson
-urllib3==1.25.3
+urllib3==1.26.5
     # via
     #   -r requirements-dev.txt
     #   requests
@@ -723,7 +728,7 @@ wcwidth==0.1.9
     # via
     #   -r requirements-dev.txt
     #   prompt-toolkit
-web3==5.18.0
+web3==5.19.0
     # via
     #   -r requirements-dev.txt
     #   raiden-contracts
@@ -735,7 +740,7 @@ websockets==8.1
     # via
     #   -r requirements-dev.txt
     #   web3
-werkzeug==1.0.1
+werkzeug==2.0.1
     # via
     #   -r requirements-dev.txt
     #   flask
diff --git a/requirements/requirements-dev.in b/requirements/requirements-dev.in
index bfd0e4d545..f53602dabb 100644
--- a/requirements/requirements-dev.in
+++ b/requirements/requirements-dev.in
@@ -20,7 +20,7 @@ isort
 readme-renderer
 pylint
 mypy
-black==20.8b1
+black==21.4b2
 typed-ast>=1.4.2
 
 # Testing
@@ -50,5 +50,5 @@ coverage
 bump2version
 
 # Test support
-matrix-synapse==1.29.0
+matrix-synapse==1.33.0
 six>=1.13.0  # work around bad deps declaration in treq, see https://github.com/twisted/treq/commit/934e127a2b915bf02be86d23f2cf8a65bcdb2533#r42782781
diff --git a/requirements/requirements-dev.txt b/requirements/requirements-dev.txt
index f119258421..506ec79c43 100644
--- a/requirements/requirements-dev.txt
+++ b/requirements/requirements-dev.txt
@@ -8,6 +8,8 @@ aioice==0.7.5
     # via
     #   -r requirements.txt
     #   aiortc
+aiortc-pyav-stub==0.1.1
+    # via -r requirements.txt
 aiortc==1.2.0
     # via -r requirements.txt
 aniso8601==7.0.0
@@ -22,7 +24,7 @@ asn1crypto==1.3.0
     # via
     #   -r requirements.txt
     #   coincurve
-astroid==2.5.2
+astroid==2.5.6
     # via pylint
 attrdict==2.0.1
     # via raiden-api-client
@@ -56,7 +58,7 @@ bitarray==1.2.1
     # via
     #   -r requirements.txt
     #   eth-account
-black==20.8b1
+black==21.4b2
     # via -r requirements-dev.in
 bleach==3.3.0
     # via
@@ -64,7 +66,7 @@ bleach==3.3.0
     #   readme-renderer
 bump2version==1.0.1
     # via -r requirements-dev.in
-cachetools==4.2.1
+cachetools==4.2.2
     # via -r requirements.txt
 canonicaljson==1.4.0
     # via
@@ -88,7 +90,7 @@ chardet==3.0.4
     # via
     #   -r requirements.txt
     #   requests
-click==8.0.0a1
+click==8.0.1
     # via
     #   -r requirements.txt
     #   black
@@ -111,10 +113,11 @@ crc32c==2.2
     # via
     #   -r requirements.txt
     #   aiortc
-cryptography==3.3.2
+cryptography==3.4.7
     # via
     #   -r requirements.txt
     #   aiortc
+    #   matrix-synapse
     #   pyopenssl
     #   service-identity
 cytoolz==0.10.1
@@ -183,7 +186,7 @@ eth-utils==1.10.0
     #   web3
 execnet==1.6.0
     # via pytest-xdist
-faker==8.1.0
+faker==8.4.0
     # via -r requirements-dev.in
 fancycompleter==0.8
     # via pdbpp
@@ -191,7 +194,7 @@ filelock==3.0.12
     # via -r requirements.txt
 flake8-bugbear==21.4.3
     # via -r requirements-dev.in
-flake8-comprehensions==3.4.0
+flake8-comprehensions==3.5.0
     # via -r requirements-dev.in
 flake8-tuple==0.4.1
     # via -r requirements-dev.in
@@ -205,9 +208,9 @@ flaky==3.7.0
     # via -r requirements-dev.in
 flask-cors==3.0.10
     # via -r requirements.txt
-flask-restful==0.3.8
+flask-restful==0.3.9
     # via -r requirements.txt
-flask==1.1.2
+flask==2.0.1
     # via
     #   -r requirements.txt
     #   flask-cors
@@ -243,7 +246,7 @@ hyperlink==21.0.0
     # via
     #   treq
     #   twisted
-hypothesis==6.10.0
+hypothesis==6.13.1
     # via -r requirements-dev.in
 idna==2.8
     # via
@@ -252,7 +255,7 @@ idna==2.8
     #   matrix-synapse
     #   requests
     #   twisted
-importlib-metadata==3.7.3
+importlib-metadata==4.0.1
     # via pluggy
 incremental==17.5.0
     # via
@@ -266,19 +269,19 @@ ipfshttpclient==0.7.0a1
     #   web3
 ipython-genutils==0.2.0
     # via traitlets
-ipython==7.22.0
+ipython==7.24.1
     # via -r requirements-dev.in
 isort==5.8.0
     # via
     #   -r requirements-dev.in
     #   pylint
-itsdangerous==1.1.0
+itsdangerous==2.0.1
     # via
     #   -r requirements.txt
     #   flask
 jedi==0.17.0
     # via ipython
-jinja2==2.11.3
+jinja2==3.0.1
     # via
     #   -r requirements.txt
     #   flask
@@ -294,7 +297,7 @@ lru-dict==1.1.6
     # via
     #   -r requirements.txt
     #   web3
-markupsafe==1.1.1
+markupsafe==2.0.1
     # via
     #   -r requirements.txt
     #   jinja2
@@ -310,9 +313,11 @@ marshmallow==3.11.1
     #   marshmallow-dataclass
     #   marshmallow-enum
     #   marshmallow-polyfield
+matplotlib-inline==0.1.2
+    # via ipython
 matrix-client==0.3.2
     # via -r requirements.txt
-matrix-synapse==1.29.0
+matrix-synapse==1.33.0
     # via -r requirements-dev.in
 mccabe==0.6.1
     # via
@@ -358,7 +363,7 @@ parsimonious==0.8.1
     #   eth-abi
 parso==0.7.0
     # via jedi
-pathspec==0.8.0
+pathspec==0.8.1
     # via black
 pdbpp==0.10.2
     # via -r requirements-dev.in
@@ -396,7 +401,7 @@ py-solc-x==1.1.0
     # via
     #   -r requirements.txt
     #   raiden-contracts
-py-spy==0.3.5
+py-spy==0.3.7
     # via -r requirements-dev.in
 py==1.10.0
     # via pytest
@@ -435,7 +440,7 @@ pylibsrtp==0.6.8
     # via
     #   -r requirements.txt
     #   aiortc
-pylint==2.7.4
+pylint==2.8.2
     # via -r requirements-dev.in
 pymacaroons==0.13.0
     # via matrix-synapse
@@ -464,7 +469,7 @@ pytest-select==0.1.2
     # via -r requirements-dev.in
 pytest-xdist==2.2.1
     # via -r requirements-dev.in
-pytest==6.2.3
+pytest==6.2.4
     # via
     #   -r requirements-dev.in
     #   pytest-forked
@@ -483,7 +488,7 @@ raiden-api-client==1.1.0
     # via -r requirements-dev.in
 raiden-contracts==0.37.5
     # via -r requirements.txt
-raiden-webui==1.1.1
+raiden-webui==1.2.1
     # via -r requirements.txt
 readme-renderer==29.0
     # via -r requirements-dev.in
@@ -523,7 +528,7 @@ simplejson==3.17.2
     # via
     #   -r requirements.txt
     #   canonicaljson
-six==1.15.0
+six==1.16.0
     # via
     #   -r requirements-dev.in
     #   -r requirements.txt
@@ -531,7 +536,6 @@ six==1.15.0
     #   automat
     #   bcrypt
     #   bleach
-    #   cryptography
     #   flake8-tuple
     #   flask-cors
     #   flask-restful
@@ -569,26 +573,26 @@ toolz==0.9.0
     #   -r requirements.txt
     #   cytoolz
 traitlets==4.3.2
-    # via ipython
+    # via
+    #   ipython
+    #   matplotlib-inline
 treq==21.1.0
     # via matrix-synapse
 twisted[tls]==21.2.0
     # via
     #   matrix-synapse
     #   treq
-typed-ast==1.4.2
+typed-ast==1.4.3
     # via
     #   -r requirements-dev.in
-    #   black
     #   mypy
 typeguard==2.11.1
     # via
     #   -r requirements.txt
     #   marshmallow-dataclass
-typing-extensions==3.7.4.3
+typing-extensions==3.10.0.0
     # via
     #   -r requirements.txt
-    #   black
     #   matrix-synapse
     #   mypy
     #   signedjson
@@ -602,7 +606,7 @@ unpaddedbase64==1.1.0
     # via
     #   matrix-synapse
     #   signedjson
-urllib3==1.25.3
+urllib3==1.26.5
     # via
     #   -r requirements.txt
     #   requests
@@ -612,7 +616,7 @@ varint==1.0.2
     #   multiaddr
 wcwidth==0.1.9
     # via prompt-toolkit
-web3==5.18.0
+web3==5.19.0
     # via
     #   -r requirements.txt
     #   raiden-contracts
@@ -622,7 +626,7 @@ websockets==8.1
     # via
     #   -r requirements.txt
     #   web3
-werkzeug==1.0.1
+werkzeug==2.0.1
     # via
     #   -r requirements.txt
     #   flask
diff --git a/requirements/requirements-docs.txt b/requirements/requirements-docs.txt
index b57c4f6a44..9b515a86ab 100644
--- a/requirements/requirements-docs.txt
+++ b/requirements/requirements-docs.txt
@@ -43,7 +43,7 @@ requests==2.25.1
     #   sphinxcontrib-images
 semantic-version==2.6.0
     # via releases
-six==1.15.0
+six==1.16.0
     # via
     #   astunparse
     #   packaging
@@ -72,7 +72,7 @@ sphinxcontrib-httpdomain==1.7.0
     #   sphinxcontrib-httpexample
 sphinxcontrib-httpexample==0.10.3
     # via -r requirements-docs.in
-sphinxcontrib-images==0.9.2
+sphinxcontrib-images==0.9.3
     # via -r requirements-docs.in
 sphinxcontrib-jsmath==1.0.1
     # via sphinx
@@ -80,7 +80,7 @@ sphinxcontrib-qthelp==1.0.3
     # via sphinx
 sphinxcontrib-serializinghtml==1.1.4
     # via sphinx
-urllib3==1.25.3
+urllib3==1.26.5
     # via requests
 wheel==0.33.4
     # via astunparse
diff --git a/requirements/requirements.in b/requirements/requirements.in
index fb070c7947..516fa153ac 100644
--- a/requirements/requirements.in
+++ b/requirements/requirements.in
@@ -1,4 +1,5 @@
 aiortc
+aiortc-pyav-stub>=0.1.1
 av>=8.0.3
 cachetools
 canonicaljson
diff --git a/requirements/requirements.txt b/requirements/requirements.txt
index 37c2d9200c..d1e131a452 100644
--- a/requirements/requirements.txt
+++ b/requirements/requirements.txt
@@ -6,6 +6,8 @@
 #
 aioice==0.7.5
     # via aiortc
+aiortc-pyav-stub==0.1.1
+    # via -r requirements.in
 aiortc==1.2.0
     # via -r requirements.in
 aniso8601==7.0.0
@@ -22,7 +24,7 @@ base58==2.0.0
     # via multiaddr
 bitarray==1.2.1
     # via eth-account
-cachetools==4.2.1
+cachetools==4.2.2
     # via -r requirements.in
 canonicaljson==1.4.0
     # via -r requirements.in
@@ -36,7 +38,7 @@ cffi==1.12.3
     #   pylibsrtp
 chardet==3.0.4
     # via requests
-click==8.0.0a1
+click==8.0.1
     # via
     #   -r requirements.in
     #   flask
@@ -49,7 +51,7 @@ colorama==0.4.4
     # via -r requirements.in
 crc32c==2.2
     # via aiortc
-cryptography==3.3.2
+cryptography==3.4.7
     # via aiortc
 cytoolz==0.10.1
     # via
@@ -103,9 +105,9 @@ filelock==3.0.12
     # via -r requirements.in
 flask-cors==3.0.10
     # via -r requirements.in
-flask-restful==0.3.8
+flask-restful==0.3.9
     # via -r requirements.in
-flask==1.1.2
+flask==2.0.1
     # via
     #   -r requirements.in
     #   flask-cors
@@ -129,9 +131,9 @@ idna==2.8
     # via requests
 ipfshttpclient==0.7.0a1
     # via web3
-itsdangerous==1.1.0
+itsdangerous==2.0.1
     # via flask
-jinja2==2.11.3
+jinja2==3.0.1
     # via
     #   -r requirements.in
     #   flask
@@ -139,7 +141,7 @@ jsonschema==3.2.0
     # via web3
 lru-dict==1.1.6
     # via web3
-markupsafe==1.1.1
+markupsafe==2.0.1
     # via jinja2
 marshmallow-dataclass[union]==8.0.0
     # via -r requirements.in
@@ -201,7 +203,7 @@ pytz==2019.1
     # via flask-restful
 raiden-contracts==0.37.5
     # via -r requirements.in
-raiden-webui==1.1.1
+raiden-webui==1.2.1
     # via -r requirements.in
 requests==2.25.1
     # via
@@ -221,9 +223,8 @@ semantic-version==2.8.5
     #   raiden-contracts
 simplejson==3.17.2
     # via canonicaljson
-six==1.15.0
+six==1.16.0
     # via
-    #   cryptography
     #   flask-cors
     #   flask-restful
     #   jsonschema
@@ -239,23 +240,23 @@ toolz==0.9.0
     # via cytoolz
 typeguard==2.11.1
     # via marshmallow-dataclass
-typing-extensions==3.7.4.3
+typing-extensions==3.10.0.0
     # via -r requirements.in
 typing-inspect==0.4.0
     # via marshmallow-dataclass
 ulid-py==1.1.0
     # via -r requirements.in
-urllib3==1.25.3
+urllib3==1.26.5
     # via requests
 varint==1.0.2
     # via multiaddr
-web3==5.18.0
+web3==5.19.0
     # via
     #   -r requirements.in
     #   raiden-contracts
 websockets==8.1
     # via web3
-werkzeug==1.0.1
+werkzeug==2.0.1
     # via flask
 zope.event==4.4
     # via gevent
diff --git a/setup.cfg b/setup.cfg
index 18eeed03c4..28fca7a86f 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -9,7 +9,6 @@ max-line-length = 99
 
 [isort]
 line_length=99
-known_future_library=future
 known_first_party=raiden,raiden_contracts,scenario_player
 default_section=THIRDPARTY
 combine_as_imports=1
@@ -37,21 +36,6 @@ omit =
     */tests/*
     */site-packages/*
 
-[tool:pytest]
-timeout_limit_for_setup_and_call = 240
-timeout_limit_teardown = 15
-norecursedirs = node_modules
-; Ignore warnings:
-; - grequests monkeypatch
-; - urllib3 unverified TLS connection
-filterwarnings =
-    ignore::gevent.monkey.MonkeyPatchWarning
-    ignore::urllib3.exceptions.InsecureRequestWarning
-markers =
-    timeout
-    asyncio: tests that require an asyncio eventloop
-junit_family = xunit1
-
 [mypy]
 ignore_missing_imports = True
 check_untyped_defs = True
diff --git a/tools/debugging/matrix/generate_messages.py b/tools/debugging/matrix/generate_messages.py
index 3a484a323a..82d34d2236 100755
--- a/tools/debugging/matrix/generate_messages.py
+++ b/tools/debugging/matrix/generate_messages.py
@@ -147,10 +147,6 @@ def ignore_messages(sync_messages: MatrixSyncMessages) -> bool:  # pylint: disab
     pass
 
 
-def ignore_member_join(room: Room) -> None:  # pylint: disable=unused-argument
-    pass
-
-
 def handle_and_time_invite(
     invite_start: float,
     client: GMatrixClient,
@@ -174,7 +170,7 @@ def send(room: Room) -> None:
 
 
 def new_user(matrix_server_url: str) -> LoggedUser:
-    client = GMatrixClient(time_messages, ignore_member_join, matrix_server_url)
+    client = GMatrixClient(time_messages, matrix_server_url)
     signer = factories.make_signer()
 
     with logtime(USER) as details:
diff --git a/tools/debugging/matrix/presence_report.py b/tools/debugging/matrix/presence_report.py
index df5611a6a7..7551f22a1a 100644
--- a/tools/debugging/matrix/presence_report.py
+++ b/tools/debugging/matrix/presence_report.py
@@ -67,7 +67,7 @@ def get_private_key(keystore_file: str, password: str) -> str:
 )
 def main(keystore_file: str, password: str, host: str, room_id: str, other_user_id: str) -> None:
     private_key = get_private_key(keystore_file, password)
-    client = GMatrixClient(lambda x: False, lambda x: None, host)
+    client = GMatrixClient(lambda x: False, host)
 
     user = login(
         client=client,
diff --git a/tools/debugging/matrix/presence_update.py b/tools/debugging/matrix/presence_update.py
index 5e6d326574..6b5f586e85 100644
--- a/tools/debugging/matrix/presence_update.py
+++ b/tools/debugging/matrix/presence_update.py
@@ -27,9 +27,7 @@
 def main() -> None:
     host = sys.argv[1]
 
-    client = GMatrixClient(
-        lambda x: False, lambda x: None, host, user_id=USER_ID, token=ACCESS_TOKEN
-    )
+    client = GMatrixClient(lambda x: False, host, user_id=USER_ID, token=ACCESS_TOKEN)
     client.join_room(ROOM_ALIAS)
 
     current_presence: Optional[str] = "offline"
diff --git a/tools/debugging/replay_wal.py b/tools/debugging/replay_wal.py
index 5e4c8002ef..b3d5acb362 100755
--- a/tools/debugging/replay_wal.py
+++ b/tools/debugging/replay_wal.py
@@ -46,7 +46,7 @@
 
 
 class Translator(dict):
-    """ Dictionary class with re substitution capabilities. """
+    """Dictionary class with re substitution capabilities."""
 
     def __init__(self, *args: Any, **kwargs: Any) -> None:
         kwargs = {k.lower(): v for k, v in args[0].items()}
@@ -72,7 +72,7 @@ def _address_rxp(self, addr: str) -> str:
         return rxp
 
     def _make_regex(self) -> None:
-        """ Compile rxp with all keys concatenated. """
+        """Compile rxp with all keys concatenated."""
         rxp = "|".join(map(self._address_rxp, self.keys()))
         self._regex = re.compile(rxp, re.IGNORECASE)
 
@@ -94,11 +94,11 @@ def __getitem__(self, key: str) -> Any:
                 raise e
 
     def __call__(self, match: re.Match) -> str:
-        """ Lookup for each rxp match. """
+        """Lookup for each rxp match."""
         return "[{}]".format(self[match.group(0).lower()])
 
     def translate(self, text: str) -> str:
-        """ Translate text. """
+        """Translate text."""
         assert self._regex is not None, "regex not set"
         return self._regex.sub(self, text)
 
diff --git a/tools/pip-compile-wrapper.py b/tools/pip-compile-wrapper.py
index 9114061792..670c8ac789 100755
--- a/tools/pip-compile-wrapper.py
+++ b/tools/pip-compile-wrapper.py
@@ -128,7 +128,7 @@ def _run_pip_compile(
         "--verbose" if verbose else "--quiet",
         *dry_run_cmd,
         *pre_cmd,
-        "--no-index",
+        "--no-emit-index-url",
         *upgrade_packages_cmd,
         *upgrade_all_cmd,
         "--output-file",
@@ -209,7 +209,7 @@ def _get_requirement_packages(
 
 
 def _get_sources_for_packages(package_names: Set[str], where: TargetType) -> Dict[str, Set[str]]:
-    """ Return source and / or target files concerned by packages """
+    """Return source and / or target files concerned by packages"""
     package_to_source = [
         (package_name, source_name)
         for source_name in SOURCE_PATHS.keys()
diff --git a/tools/pre-commit/pre-commit-check-versions.py b/tools/pre-commit/pre-commit-check-versions.py
new file mode 100644
index 0000000000..37f44dd8bf
--- /dev/null
+++ b/tools/pre-commit/pre-commit-check-versions.py
@@ -0,0 +1,156 @@
+#!/usr/bin/env python3
+"""
+Helper to check if the tool versions specified in the pre-commit config file
+match the general project requirements.
+"""
+
+import sys
+from dataclasses import dataclass, field
+from enum import Enum
+from pathlib import Path
+from typing import Dict, List, Optional
+
+import click
+import yaml
+from packaging.requirements import Requirement
+from packaging.version import Version, parse
+
+PRE_COMMIT_CONFG_PATH = Path(".pre-commit-config.yaml")
+REQUIREMENTS_DEV_PATH = Path("requirements", "requirements-dev.txt")
+
+
+@dataclass(frozen=True)
+class ToolConfig:
+    name: str
+    url: str
+
+
+class Tool(Enum):
+    BLACK = ToolConfig(name="black", url="https://github.com/python/black")
+    FLAKE8 = ToolConfig(name="flake8", url="https://gitlab.com/PyCQA/flake8")
+    ISORT = ToolConfig(name="isort", url="git://github.com/pre-commit/mirrors-isort")
+    MYPY = ToolConfig(name="mypy", url="https://github.com/pre-commit/mirrors-mypy")
+    PYLINT = ToolConfig(name="pylint", url="https://github.com/PyCQA/pylint")
+
+
+@dataclass
+class ToolVersion:
+    tool: Tool
+    version: Version
+    additional_deps: List[Requirement] = field(default_factory=list)
+
+
+_TOOL_BY_REPO_URL: Dict[str, Tool] = {tool.value.url: tool for tool in Tool}
+
+
+def _find_path_from_project_root(path_fragment: Path) -> Optional[Path]:
+    parents = Path(__file__).resolve().parent.parents
+    for parent in parents:
+        config_file = parent.joinpath(path_fragment)
+        if config_file.exists():
+            return config_file
+    return None
+
+
+def find_pre_commit_config_file() -> Path:
+    config_file = _find_path_from_project_root(PRE_COMMIT_CONFG_PATH)
+    if config_file is None:
+        raise ValueError("No pre-commit config file found in any parent directory.")
+    return config_file
+
+
+def parse_pre_commit_config_tool_versions(config_file: Path) -> List[ToolVersion]:
+    parsed_config = yaml.safe_load(config_file.read_text())
+    tool_versions = []
+    for repo in parsed_config["repos"]:
+        tool = _TOOL_BY_REPO_URL.get(repo["repo"])
+        if tool is None:
+            continue
+        additional_deps = []
+        for hook in repo["hooks"]:
+            if hook["id"] == tool.value.name:
+                for additional_dep in hook.get("additional_dependencies", []):
+                    additional_deps.append(Requirement(additional_dep))
+        tool_versions.append(
+            ToolVersion(
+                tool=tool,
+                version=parse(repo["rev"]),
+                additional_deps=additional_deps,
+            )
+        )
+    return tool_versions
+
+
+def find_requirements_dev_file() -> Path:
+    req_file = _find_path_from_project_root(REQUIREMENTS_DEV_PATH)
+    if req_file is None:
+        raise ValueError(
+            f"Requirements file '{REQUIREMENTS_DEV_PATH!s}' not found in any parent directory."
+        )
+    return req_file
+
+
+def parse_requirements_dev(requirements_dev_path: Path) -> Dict[str, Requirement]:
+    with requirements_dev_path.open("rt") as requirements_file:
+        requirements = {}
+        for line in requirements_file:
+            requirement_str, *_ = line.strip().partition("#")
+            if requirement_str:
+                requirement = Requirement(requirement_str)
+                requirements[requirement.name] = requirement
+    return requirements
+
+
+def check_pre_commit_tool_versions(
+    tool_versions: List[ToolVersion], requirements: Dict[str, Requirement]
+) -> List[str]:
+    errors = []
+    for tool_version in tool_versions:
+        tool = tool_version.tool.value
+        tool_requirement = requirements.get(tool.name)
+        if tool_requirement is None:
+            errors.append(f"No project requirement for tool '{tool.name}' found.")
+            continue
+        if tool_version.version not in tool_requirement.specifier:
+            errors.append(
+                f"Tool '{tool.name}' ({tool_version.version}) is out of sync with project "
+                f"requirement ({tool_requirement.specifier})."
+            )
+        for additional_dep in tool_version.additional_deps:
+            specifiers = list(additional_dep.specifier)
+            if len(specifiers) != 1 or specifiers[0].operator != "==":
+                errors.append(
+                    f"Can only process a single strict equality constraint for "
+                    f"'additional_dependencies' (in '{tool.name}' -> '{additional_dep}')"
+                )
+                continue
+            additional_dep_requirement = requirements.get(additional_dep.name)
+            if additional_dep_requirement is None:
+                errors.append(
+                    f"Can't find additional dependency '{additional_dep}' of '{tool.name}' in "
+                    f"project requirements."
+                )
+                continue
+            if Version(specifiers[0].version) not in additional_dep_requirement.specifier:
+                errors.append(
+                    f"Additional dependency '{additional_dep}' of '{tool.name}' is out of sync "
+                    f"with project requirement '{additional_dep_requirement}'."
+                )
+                continue
+    return errors
+
+
+@click.command()
+def main() -> None:
+    tool_versions = parse_pre_commit_config_tool_versions(find_pre_commit_config_file())
+    project_requirements = parse_requirements_dev(find_requirements_dev_file())
+    errors = check_pre_commit_tool_versions(tool_versions, project_requirements)
+    if errors:
+        click.secho("pre-commit config is out of sync!", fg="red")
+        click.echo("  - ", nl=False)
+        click.echo("\n  - ".join(click.style(error, fg="yellow") for error in errors))
+        sys.exit(1)
+
+
+if __name__ == "__main__":
+    main()
diff --git a/tools/pyinstaller_hooks/hook-av.py b/tools/pyinstaller_hooks/hook-av.py
new file mode 100644
index 0000000000..d2a98cd348
--- /dev/null
+++ b/tools/pyinstaller_hooks/hook-av.py
@@ -0,0 +1,4 @@
+from PyInstaller.utils.hooks import collect_submodules
+
+
+hiddenimports = collect_submodules("av")
diff --git a/tools/pyinstaller_hooks/runtime_aiortc_pyav_stub.py b/tools/pyinstaller_hooks/runtime_aiortc_pyav_stub.py
new file mode 100644
index 0000000000..1017065774
--- /dev/null
+++ b/tools/pyinstaller_hooks/runtime_aiortc_pyav_stub.py
@@ -0,0 +1,4 @@
+import aiortc_pyav_stub
+
+
+aiortc_pyav_stub.install_as_av()
diff --git a/tools/pyinstaller_hooks/runtime_gevent_monkey.py b/tools/pyinstaller_hooks/runtime_gevent_monkey.py
index 0d4cb6375f..afea202cbd 100644
--- a/tools/pyinstaller_hooks/runtime_gevent_monkey.py
+++ b/tools/pyinstaller_hooks/runtime_gevent_monkey.py
@@ -1,7 +1,3 @@
-from gevent.monkey import patch_all  # isort:skip # noqa
+from gevent.monkey import patch_all
 
-patch_all()  # isort:skip # noqa
-
-from raiden.network.transport.matrix.rtc.utils import setup_asyncio_event_loop
-
-setup_asyncio_event_loop()
+patch_all()
diff --git a/tools/pylint/__init__.py b/tools/pylint/__init__.py
index 95754d25b6..a769f8876e 100644
--- a/tools/pylint/__init__.py
+++ b/tools/pylint/__init__.py
@@ -18,7 +18,7 @@ def find_parent(node: NodeNG, scope_type: T_NODE) -> Optional[T_NODE]:
 
 
 def ignore_tests(func: Callable) -> Callable:
-    """ Decorator that ignores nodes below the raiden.tests module. """
+    """Decorator that ignores nodes below the raiden.tests module."""
 
     @wraps(func)
     def decorator(self, node: NodeNG) -> Any:


Test patch:
diff --git a/raiden/tests/conftest.py b/conftest.py
similarity index 94%
rename from raiden/tests/conftest.py
rename to conftest.py
index 4233caa970..2ee8a68f45 100644
--- a/raiden/tests/conftest.py
+++ b/conftest.py
@@ -1,27 +1,20 @@
 # pylint: disable=wrong-import-position,redefined-outer-name,unused-wildcard-import,wildcard-import
-import gevent  # isort:skip # noqa
-from gevent import monkey, Timeout  # isort:skip # noqa
+# type: ignore
+from gevent import monkey
 
-monkey.patch_all(subprocess=False, thread=False)  # isort:skip # noqa
+monkey.patch_all(subprocess=False, thread=False)
 
-import asyncio
-import contextlib
-import datetime
-import os
-import signal
-import subprocess
-import sys
-import time
+# isort:split
 
-import pytest
-import structlog
+import aiortc_pyav_stub
 
-from raiden.network.transport.matrix.rtc.aiogevent import yield_future
-from raiden.network.transport.matrix.rtc.utils import (
-    ASYNCIO_LOOP_RUNNING_TIMEOUT,
-    setup_asyncio_event_loop,
-)
-from raiden.utils.ethereum_clients import VersionSupport
+# Install the av replacement stub to make sure we catch possible version
+# upgrade breakages
+aiortc_pyav_stub.install_as_av()
+
+# isort: split
+
+import pytest
 
 # Execute these before the raiden imports because rewrites can't work after the
 # module has been imported.
@@ -35,22 +28,41 @@
 pytest.register_assert_rewrite("raiden.tests.utils.smoketest")
 pytest.register_assert_rewrite("raiden.tests.utils.transfer")
 
-from raiden.constants import (  # isort:skip
+# isort:split
+
+import asyncio
+import contextlib
+import datetime
+import os
+import signal
+import subprocess
+import sys
+import time
+
+import gevent
+import structlog
+from gevent import Timeout
+
+from raiden.constants import (
     HIGHEST_SUPPORTED_GETH_VERSION,
     HIGHEST_SUPPORTED_PARITY_VERSION,
     LOWEST_SUPPORTED_GETH_VERSION,
     LOWEST_SUPPORTED_PARITY_VERSION,
     EthClient,
 )
-from raiden.log_config import configure_logging  # isort:skip
-from raiden.tests.fixtures.blockchain import *  # noqa: F401,F403  # isort:skip
-from raiden.tests.fixtures.variables import *  # noqa: F401,F403  # isort:skip
-from raiden.tests.integration.exception import RetryTestError  # isort:skip
-from raiden.tests.utils.transport import make_requests_insecure  # isort:skip
-from raiden.utils.cli import LogLevelConfigType  # isort:skip
-from raiden.utils.debugging import enable_gevent_monitoring_signal  # isort:skip
-from raiden.utils.ethereum_clients import is_supported_client  # isort:skip
-
+from raiden.log_config import configure_logging
+from raiden.network.transport.matrix.rtc.aiogevent import yield_future
+from raiden.network.transport.matrix.rtc.utils import (
+    ASYNCIO_LOOP_RUNNING_TIMEOUT,
+    setup_asyncio_event_loop,
+)
+from raiden.tests.fixtures.blockchain import *  # noqa: F401,F403
+from raiden.tests.fixtures.variables import *  # noqa: F401,F403
+from raiden.tests.integration.exception import RetryTestError
+from raiden.tests.utils.transport import make_requests_insecure
+from raiden.utils.cli import LogLevelConfigType
+from raiden.utils.debugging import enable_gevent_monitoring_signal
+from raiden.utils.ethereum_clients import VersionSupport, is_supported_client
 
 log = structlog.get_logger()
 
@@ -370,14 +382,20 @@ def set_item_timeouts(item):
     timeout_limit_setup_and_call = item.config.getini("timeout_limit_for_setup_and_call")
 
     if timeout_limit_setup_and_call == "":
-        raise Exception("timeout_limit_for_setup_and_call must be set in setup.cfg")
+        raise RuntimeError(
+            "timeout_limit_for_setup_and_call must be set in section "
+            "'tool.pytest.ini_options' in pyproject.toml"
+        )
 
     timeout_limit_setup_and_call = float(timeout_limit_setup_and_call)
 
     timeout_limit_teardown = item.config.getini("timeout_limit_teardown")
 
     if timeout_limit_teardown == "":
-        raise Exception("timeout_limit_teardown must be set in setup.cfg")
+        raise RuntimeError(
+            "timeout_limit_teardown must be set in section "
+            "'tool.pytest.ini_options' in pyproject.toml"
+        )
 
     timeout_limit_teardown = float(timeout_limit_teardown)
 
@@ -388,7 +406,7 @@ def set_item_timeouts(item):
     timeout_setup_and_call = timeout_from_marker(marker) or timeout_limit_setup_and_call
 
     if timeout_setup_and_call > timeout_limit_setup_and_call:
-        raise Exception(
+        raise RuntimeError(
             f"Invalid value for the timeout marker {timeout_setup_and_call}. This "
             f"value must be smaller than {timeout_limit_setup_and_call}. This is "
             f"necessary because the runtime of a test has to be synchronized with "
@@ -400,10 +418,10 @@ def set_item_timeouts(item):
         )
 
     if timeout_setup_and_call <= 0:
-        raise Exception("timeout must not be negative")
+        raise RuntimeError("timeout must not be negative")
 
     if timeout_teardown <= 0:
-        raise Exception("timeout_limit_teardown must not be negative")
+        raise RuntimeError("timeout_limit_teardown must not be negative")
 
     item.timeout_setup_and_call = timeout_setup_and_call
     item.timeout_teardown = timeout_teardown
diff --git a/docs/using-raiden-on-testnet/quick-start-on-goerli-testnet.rst b/docs/installation/quick-start-goerli/quick-start-on-goerli-testnet.rst
similarity index 92%
rename from docs/using-raiden-on-testnet/quick-start-on-goerli-testnet.rst
rename to docs/installation/quick-start-goerli/quick-start-on-goerli-testnet.rst
index b4ee6cb36c..a39877c939 100644
--- a/docs/using-raiden-on-testnet/quick-start-on-goerli-testnet.rst
+++ b/docs/installation/quick-start-goerli/quick-start-on-goerli-testnet.rst
@@ -1,7 +1,7 @@
 .. _quickstart_goerli:
 
 Quick Start on Görli Testnet
-============================
+############################
 
 You might want to try out Raiden on a testnet before using it on mainnet
 or might need a development environment. For this reasons we provide a
@@ -10,17 +10,17 @@ set up on this environment is to use the Raiden Wizard for Görli. This
 page will guide you through the process.
 
 Prerequisites 
---------------
+=============
 
 In order to use the Raiden Wizard and Raiden you'll need:
 
 -  A computer running **Linux** or **macOS**.
 -  An Infura ID. Click
-   :doc:`here <../installation/quick-start/infura-quick-start>` to get
+   :ref:`here <infura_tutorial>` to get
    one.
 
 Download the Wizard
--------------------
+===================
 
 Click on your operating system to download the Wizard:
 
@@ -28,7 +28,7 @@ Click on your operating system to download the Wizard:
 -  `Linux <https://github.com/raiden-network/raiden-wizard/releases/download/v1.1.0/raiden_wizard_goerli_linux-1.1.0.tar.gz>`__
 
 Run the Wizard
---------------
+==============
 
 1. Extract and run the ``raiden_wizard`` program.
 2. Your web browser should open a new window with the Raiden Wizard. If
@@ -54,7 +54,7 @@ The Raiden Wizard will do the following for you:
 
 
 Next steps
-----------
+==========
 
 After the completion of the Wizard you will get redirected to the web
 interface of your Raiden node. See :ref:`this
@@ -68,7 +68,7 @@ tokens to it. The payment should show up on the hub's
 `webpage <https://hub.raiden.network>`__.
 
 Restart your Raiden node
-------------------------
+========================
 
 After you have shut down your Raiden node, the Raiden Wizard can be used
 as a launcher for your node. When you start the Wizard, it will
diff --git a/docs/other/slow-network-test.rst b/docs/other/slow-network-test.rst
new file mode 100644
index 0000000000..cd494d851c
--- /dev/null
+++ b/docs/other/slow-network-test.rst
@@ -0,0 +1,70 @@
+Testing Raiden on a slow network
+================================
+
+In order to analyze Raiden performance on slow networks, it is useful to have a way of
+introducing latencies. On Linux this can be achieved by making use of the ``tc`` utility.
+
+.. note::
+
+   These instructions are Linux-specific. You will need the ``ip``, ``tc`` and
+   ``iptables`` utilities. If you're using Arch Linux, you can get these by
+   installing the ``iproute2`` and ``iptables`` packages.
+
+The following script can be used to prepare the interfaces:
+
+.. code:: bash
+
+    #!/usr/bin/env bash
+
+    # The network interface to the outside, i.e. the Internet.
+    IFACE="wlan0"
+
+    IP=$(ip addr show dev ${IFACE} | grep -Po 'inet \K[\d.]+')
+    NSNAME="slow"
+
+    VETHA="veth-a"
+    VETHB="veth-b"
+    VETHPREFIX="192.168.163.0/24"
+    VETHNETMASK="255.255.255.0"
+    VETHAIP="192.168.163.1"
+    VETHBIP="192.168.163.254"
+
+    ip netns add ${NSNAME}
+    ip link add ${VETHA} type veth peer name ${VETHB}
+    ip link set ${VETHA} netns ${NSNAME}
+
+    ip netns exec ${NSNAME} ip link set dev ${VETHA} up
+    ip netns exec ${NSNAME} ip addr add ${VETHAIP}/24 brd ${VETHNETMASK} dev ${VETHA}
+
+    ip link set dev ${VETHB} up
+    ip addr add ${VETHBIP}/24 brd ${VETHNETMASK} dev ${VETHB}
+
+    ip netns exec ${NSNAME} route add default gw ${VETHBIP} dev ${VETHA}
+    echo 1 > /proc/sys/net/ipv4/ip_forward
+    iptables -t nat -A POSTROUTING -s ${VETHPREFIX} -o ${IFACE} -j SNAT --to-source ${IP}
+
+
+.. note::
+
+   All of the things here require root privileges.
+
+The script will create a virtual Ethernet interface pair (veth-a, veth-b) and move one of the
+interfaces (veth-b) into a newly created network namespace. After setting up the interfaces'
+addresses and adding the default route, the script will configure packet forwarding to the
+real interface (wlan0) connected to the Internet.
+
+Once the interfaces are prepared, we can simulate a network delay of, say, 200ms by
+using tc (replace ``${VETHB}`` with its value defined in the above script):
+
+.. code:: bash
+
+    tc qdisc add dev ${VETHB} root netem delay 200ms
+
+Now we just need to start Raiden from within the network namespace that we created:
+
+.. code:: bash
+
+    ip netns exec ${NSNAME} bash
+    # switch to a normal user
+    # enter virtual environment
+    # run Raiden
diff --git a/docs/testing.rst b/docs/testing.rst
index 4a67871941..621122ffb1 100644
--- a/docs/testing.rst
+++ b/docs/testing.rst
@@ -1,5 +1,5 @@
-How is Raiden tested.
-#####################
+How is Raiden tested
+####################
 
 Raiden tests are split into these categories. The following categories are
 ordered from the faster and easier to debug to the slower and harder to debug:
@@ -70,7 +70,7 @@ Here is an overview of the fixture system:
   start of the test.
 - The fixtures responsible to start a private chain are defined in
   ``raiden/tests/integration/fixtures/blockchain.py``. These will start a new
-  private chain, either ``geth`` or ``parity``, with
+  private chain, either ``geth`` or ``openethereum``, with
   ``blockchain_number_of_nodes`` nodes. This private chain will have one
   prefunded account for each Raiden node, which allows these nodes to send
   on-chain transactions.
diff --git a/raiden/tests/benchmark/_codespeed.py b/raiden/tests/benchmark/_codespeed.py
new file mode 100644
index 0000000000..9d1374369d
--- /dev/null
+++ b/raiden/tests/benchmark/_codespeed.py
@@ -0,0 +1,33 @@
+import json
+import os
+
+import requests
+
+
+try:
+    _CODESPEED_USER = os.environ["CODESPEED_USER"]
+
+    _CODESPEED_PASSWORD = os.environ["CODESPEED_PASSWORD"]
+
+    _BENCHMARK_HOST = os.environ["BENCHMARK_HOST"]
+except KeyError:
+    print("Codespeed environment variables not available, posting results would fail.")
+
+
+def post_result(codespeed_url, commit_id, branch, bench_name, value):
+    data = [
+        {
+            "commitid": commit_id,
+            "project": "raiden",
+            "branch": branch,
+            "executable": "raiden",
+            "benchmark": bench_name,
+            "environment": _BENCHMARK_HOST,
+            "result_value": value,
+        }
+    ]
+
+    data_ = {"json": json.dumps(data)}
+    url = codespeed_url + "/result/add/json/"
+    resp = requests.post(url, data=data_, auth=(_CODESPEED_USER, _CODESPEED_PASSWORD))
+    resp.raise_for_status()
diff --git a/raiden/tests/benchmark/conftest.py b/raiden/tests/benchmark/conftest.py
new file mode 100644
index 0000000000..570ba9d90e
--- /dev/null
+++ b/raiden/tests/benchmark/conftest.py
@@ -0,0 +1,77 @@
+import contextlib
+import shlex
+import subprocess
+import time
+
+import pytest
+
+from raiden.tests.integration.fixtures.blockchain import *  # noqa: F401,F403
+from raiden.tests.integration.fixtures.raiden_network import *  # noqa: F401,F403
+from raiden.tests.integration.fixtures.smartcontracts import *  # noqa: F401,F403
+from raiden.tests.integration.fixtures.transport import *  # noqa: F401,F403
+
+from . import _codespeed
+
+
+def pytest_addoption(parser):
+    parser.addoption(
+        "--codespeed-url",
+        action="store",
+        default=None,
+        help="the URL of a running Codespeed instance, " "e.g. http://127.0.0.1:8000",
+    )
+    parser.addoption(
+        "--output-benchmark-results",
+        action="store",
+        default=None,
+        help="the path to write benchmark results to",
+    )
+
+
+def pytest_collection_modifyitems(items):
+    for item in items:
+        item.add_marker(pytest.mark.asyncio)
+
+
+_TIMES = {}
+
+
+_COMMIT_ID = subprocess.check_output(shlex.split("git rev-parse HEAD")).decode().strip()
+
+
+# "git branch --show-current" would be simpler, but CI git version is too old for that.
+_BRANCH = subprocess.check_output(shlex.split("git rev-parse --abbrev-ref HEAD")).decode().strip()
+
+
+@pytest.fixture
+def bench(request):
+    node_name = request.node.name
+
+    @contextlib.contextmanager
+    def wrapper(s=None):
+        t1 = time.perf_counter()
+        yield
+        t2 = time.perf_counter()
+        dt = t2 - t1
+        bench_name = "%s[%s]" % (node_name, s) if s is not None else node_name
+        print(f"{bench_name}: {dt}")
+        _TIMES[bench_name] = dt
+
+        url = request.config.getoption("--codespeed-url")
+        if url is not None:
+            _codespeed.post_result(url, _COMMIT_ID, _BRANCH, bench_name, dt)
+
+        path = request.config.getoption("--output-benchmark-results")
+        if path is not None:
+            with open(path, "a") as f:
+                f.write(f"{bench_name}, {dt}\n")
+
+    return wrapper
+
+
+def pytest_terminal_summary(
+    terminalreporter, exitstatus, config
+):  # pylint: disable=unused-argument
+    terminalreporter.section("benchmark report")
+    for bench_name, time_ in _TIMES.items():
+        terminalreporter.write_line(f"{bench_name}: {time_}")
diff --git a/raiden/tests/benchmark/test_mediated_transfer.py b/raiden/tests/benchmark/test_mediated_transfer.py
new file mode 100644
index 0000000000..b4b93c497c
--- /dev/null
+++ b/raiden/tests/benchmark/test_mediated_transfer.py
@@ -0,0 +1,63 @@
+from typing import List
+
+import pytest
+
+from raiden.raiden_service import RaidenService
+from raiden.tests.utils.detect_failure import raise_on_failure
+from raiden.tests.utils.network import CHAIN
+from raiden.tests.utils.transfer import (
+    assert_succeeding_transfer_invariants,
+    block_timeout_for_transfer_by_secrethash,
+    transfer,
+    wait_assert,
+)
+from raiden.transfer import views
+from raiden.utils.typing import PaymentAmount, PaymentID
+
+
+@raise_on_failure
+@pytest.mark.parametrize("channels_per_node", [CHAIN])
+@pytest.mark.parametrize("number_of_nodes", [3, 4, 5])
+def test_mediated_transfer(
+    raiden_network: List[RaidenService],
+    number_of_nodes,
+    deposit,
+    token_addresses,
+    network_wait,
+    bench,
+):
+    apps = raiden_network
+    token_address = token_addresses[0]
+    chain_state = views.state_from_raiden(apps[0])
+    token_network_registry_address = apps[0].default_registry.address
+    token_network_address = views.get_token_network_address_by_token_address(
+        chain_state, token_network_registry_address, token_address
+    )
+
+    with bench():
+        amount = PaymentAmount(10)
+        with bench("transfer"):
+            secrethash = transfer(
+                initiator_app=apps[0],
+                target_app=apps[-1],
+                token_address=token_address,
+                amount=amount,
+                identifier=PaymentID(1),
+                timeout=network_wait * number_of_nodes,
+                routes=[apps],
+            )
+
+        while len(apps) > 1:
+            app1 = apps.pop(0)
+            app2 = apps[0]
+            with block_timeout_for_transfer_by_secrethash(app2, secrethash):
+                wait_assert(
+                    assert_succeeding_transfer_invariants,
+                    token_network_address,
+                    app1,
+                    deposit - amount,
+                    [],
+                    app2,
+                    deposit + amount,
+                    [],
+                )
diff --git a/raiden/tests/fixtures/variables.py b/raiden/tests/fixtures/variables.py
index 20e0c32cd1..9feeb0c0d2 100644
--- a/raiden/tests/fixtures/variables.py
+++ b/raiden/tests/fixtures/variables.py
@@ -169,7 +169,7 @@ def logs_storage(request, tmpdir) -> str:
 
 @pytest.fixture
 def deposit() -> TokenAmount:
-    """ Raiden chain default deposit. """
+    """Raiden chain default deposit."""
     # Arbitrary initial balance for each channel, using a small number for
     # easier calculations during testing
     return TokenAmount(200)
@@ -177,25 +177,25 @@ def deposit() -> TokenAmount:
 
 @pytest.fixture
 def number_of_tokens() -> int:
-    """ Number of tokens pre-registered in the test Registry. """
+    """Number of tokens pre-registered in the test Registry."""
     return 1
 
 
 @pytest.fixture
 def register_tokens() -> bool:
-    """ Should fixture generated tokens be registered with raiden. """
+    """Should fixture generated tokens be registered with raiden."""
     return True
 
 
 @pytest.fixture
 def number_of_nodes() -> int:
-    """ Number of raiden nodes in the test network. """
+    """Number of raiden nodes in the test network."""
     return 3
 
 
 @pytest.fixture
 def channels_per_node() -> int:
-    """ Number of pre-created channels per test raiden node. """
+    """Number of pre-created channels per test raiden node."""
     return 1
 
 
@@ -242,7 +242,7 @@ def network_wait():
 
 @pytest.fixture
 def private_keys(number_of_nodes, privatekey_seed):
-    """ Private keys for each raiden node. """
+    """Private keys for each raiden node."""
 
     # Note: The fixtures depend on the order of the private keys
     result = [
@@ -296,7 +296,7 @@ def blockchain_key_seed(request):
 
 @pytest.fixture(scope="session")
 def port_generator(request, worker_id) -> Iterator[Port]:
-    """ count generator used to get a unique port number. """
+    """count generator used to get a unique port number."""
     if worker_id == "master":
         # xdist is not in use to run parallel tests
         port_offset = 0
@@ -345,7 +345,7 @@ def unrecoverable_error_should_crash():
 
 @pytest.fixture
 def transport():
-    """ 'all' replaced by parametrize in conftest.pytest_generate_tests """
+    """'all' replaced by parametrize in conftest.pytest_generate_tests"""
     return "matrix"
 
 
diff --git a/raiden/tests/fuzz/conftest.py b/raiden/tests/fuzz/conftest.py
index 9e119b9016..2bd20a0e41 100644
--- a/raiden/tests/fuzz/conftest.py
+++ b/raiden/tests/fuzz/conftest.py
@@ -3,5 +3,5 @@
 
 @pytest.fixture(autouse=True)
 def override_capture_setting_for_hypothesis_tests(request):
-    """override the general setting to see failed paths generated by Hypothesis """
+    """override the general setting to see failed paths generated by Hypothesis"""
     request.config.option.showcapture = "all"
diff --git a/raiden/tests/fuzz/test_state_changes.py b/raiden/tests/fuzz/test_state_changes.py
index dff1f8d090..b6fde5f214 100644
--- a/raiden/tests/fuzz/test_state_changes.py
+++ b/raiden/tests/fuzz/test_state_changes.py
@@ -160,7 +160,7 @@ class Client:
     transfer_order: TransferOrder = field(default_factory=TransferOrder)
 
     def assert_monotonicity_invariants(self):
-        """ Assert all monotonicity properties stated in Raiden specification """
+        """Assert all monotonicity properties stated in Raiden specification"""
         for (
             address,
             netting_channel,
@@ -195,7 +195,7 @@ def assert_monotonicity_invariants(self):
             self.partner_previous_unclaimed[address] = partner_unclaimed
 
     def assert_channel_state_invariants(self):
-        """ Assert all channel state invariants given in the Raiden specification """
+        """Assert all channel state invariants given in the Raiden specification"""
         for netting_channel in self.address_to_channel.values():  # pylint: disable=no-member
             our_state = netting_channel.our_state
             partner_state = netting_channel.partner_state
diff --git a/raiden/tests/integration/api/rest/test_api_wrapper.py b/raiden/tests/integration/api/rest/test_api_wrapper.py
index ad6bafd0c6..9a14a71fa0 100644
--- a/raiden/tests/integration/api/rest/test_api_wrapper.py
+++ b/raiden/tests/integration/api/rest/test_api_wrapper.py
@@ -15,8 +15,10 @@
 @pytest.mark.parametrize("number_of_nodes", [2])
 @pytest.mark.parametrize("channels_per_node", [0])
 @pytest.mark.parametrize("enable_rest_api", [True])
-def test_api_wrapper(raiden_network, unregistered_custom_token, retry_timeout):
+def test_api_wrapper(raiden_network, unregistered_custom_token, retry_timeout, pfs_mock):
     app0, app1 = raiden_network
+    pfs_mock.add_apps(raiden_network)
+
     address1 = to_checksum_address(app0.address)
     address2 = to_checksum_address(app1.address)
 
diff --git a/raiden/tests/integration/api/rest/test_channel.py b/raiden/tests/integration/api/rest/test_channel.py
index ed7886b835..969a90a7dc 100644
--- a/raiden/tests/integration/api/rest/test_channel.py
+++ b/raiden/tests/integration/api/rest/test_channel.py
@@ -343,7 +343,9 @@ def test_api_channel_open_and_deposit_race(
 @pytest.mark.parametrize("channels_per_node", [0])
 @pytest.mark.parametrize("enable_rest_api", [True])
 def test_api_channel_open_close_and_settle(
-    api_server_test_instance: APIServer, token_addresses, reveal_timeout
+    api_server_test_instance: APIServer,
+    token_addresses,
+    reveal_timeout,
 ):
     # let's create a new channel
     partner_address = "0x61C808D82A3Ac53231750daDc13c777b59310bD9"
@@ -689,9 +691,14 @@ def test_api_channel_state_change_errors(
 @pytest.mark.parametrize("deposit", [1000])
 @pytest.mark.parametrize("enable_rest_api", [True])
 def test_api_channel_withdraw(
-    api_server_test_instance: APIServer, raiden_network: List[RaidenService], token_addresses
+    api_server_test_instance: APIServer,
+    raiden_network: List[RaidenService],
+    token_addresses,
+    pfs_mock,
 ):
     _, app1 = raiden_network
+    pfs_mock.add_apps(raiden_network)
+
     token_address = token_addresses[0]
     partner_address = app1.address
 
@@ -748,6 +755,36 @@ def test_api_channel_withdraw(
     assert_response_with_error(response, HTTPStatus.CONFLICT)
 
 
+@raise_on_failure
+@pytest.mark.parametrize("number_of_nodes", [2])
+@pytest.mark.parametrize("deposit", [1000])
+@pytest.mark.parametrize("enable_rest_api", [True])
+def test_api_channel_withdraw_with_offline_partner(
+    api_server_test_instance: APIServer,
+    raiden_network: List[RaidenService],
+    token_addresses,
+    pfs_mock,
+):
+    app0, app1 = raiden_network
+    pfs_mock.add_apps([app0])
+
+    token_address = token_addresses[0]
+    partner_address = app1.address
+
+    # Withdraw when the partner node is offline
+    request = grequests.patch(
+        api_url_for(
+            api_server_test_instance,
+            "channelsresourcebytokenandpartneraddress",
+            token_address=token_address,
+            partner_address=partner_address,
+        ),
+        json=dict(total_withdraw="500"),
+    )
+    response = request.send().response
+    assert_response_with_error(response, HTTPStatus.CONFLICT)
+
+
 @raise_on_failure
 @pytest.mark.parametrize("number_of_nodes", [2])
 @pytest.mark.parametrize("deposit", [0])
diff --git a/raiden/tests/integration/api/rest/test_payments.py b/raiden/tests/integration/api/rest/test_payments.py
index db690622a4..e10fc24d51 100644
--- a/raiden/tests/integration/api/rest/test_payments.py
+++ b/raiden/tests/integration/api/rest/test_payments.py
@@ -30,12 +30,17 @@
 @pytest.mark.parametrize("number_of_nodes", [2])
 @pytest.mark.parametrize("enable_rest_api", [True])
 def test_api_payments_target_error(
-    api_server_test_instance: APIServer, raiden_network: List[RaidenService], token_addresses
+    api_server_test_instance: APIServer,
+    raiden_network: List[RaidenService],
+    token_addresses,
+    pfs_mock,
 ) -> None:
     _, app1 = raiden_network
     token_address = token_addresses[0]
     target_address = app1.address
 
+    pfs_mock.add_apps(raiden_network)
+
     # stop app1 to force an error
     app1.stop()
 
@@ -60,6 +65,7 @@ def test_api_payments(
     raiden_network: List[RaidenService],
     token_addresses,
     deposit,
+    pfs_mock,
 ) -> None:
     _, app1 = raiden_network
     amount = 100
@@ -67,6 +73,8 @@ def test_api_payments(
     token_address = token_addresses[0]
     target_address = app1.address
 
+    pfs_mock.add_apps(raiden_network)
+
     our_address = api_server_test_instance.rest_api.raiden_api.address
 
     payment = {
@@ -111,7 +119,8 @@ def test_api_payments(
     json_response = get_json_response(response)
     assert_payment_secret_and_hash(json_response, payment)
 
-    # Test that trying out a payment with an amount higher than what is available returns an error
+    # Test that trying out a payment with an amount higher than what is available
+    # returns an error
     payment["amount"] = str(deposit)
     request = grequests.post(
         api_url_for(
@@ -143,7 +152,10 @@ def test_api_payments(
 @pytest.mark.parametrize("number_of_nodes", [2])
 @pytest.mark.parametrize("enable_rest_api", [True])
 def test_api_payments_secret_hash_errors(
-    api_server_test_instance: APIServer, raiden_network: List[RaidenService], token_addresses
+    api_server_test_instance: APIServer,
+    raiden_network: List[RaidenService],
+    token_addresses,
+    pfs_mock,
 ):
     _, app1 = raiden_network
     token_address = token_addresses[0]
@@ -154,6 +166,8 @@ def test_api_payments_secret_hash_errors(
     short_secret = "0x123"
     short_secret_hash = "Short secret hash"
 
+    pfs_mock.add_apps(raiden_network)
+
     request = grequests.post(
         api_url_for(
             api_server_test_instance,
@@ -201,7 +215,11 @@ def test_api_payments_secret_hash_errors(
             token_address=to_checksum_address(token_address),
             target_address=to_checksum_address(target_address),
         ),
-        json={"amount": DEFAULT_AMOUNT, "identifier": DEFAULT_ID, "secret_hash": bad_secret_hash},
+        json={
+            "amount": DEFAULT_AMOUNT,
+            "identifier": DEFAULT_ID,
+            "secret_hash": bad_secret_hash,
+        },
     )
     response = request.send().response
     assert_proper_response(response, status_code=HTTPStatus.BAD_REQUEST)
@@ -228,7 +246,10 @@ def test_api_payments_secret_hash_errors(
 @pytest.mark.parametrize("number_of_nodes", [2])
 @pytest.mark.parametrize("enable_rest_api", [True])
 def test_api_payments_with_secret_no_hash(
-    api_server_test_instance: APIServer, raiden_network: List[RaidenService], token_addresses
+    api_server_test_instance: APIServer,
+    raiden_network: List[RaidenService],
+    token_addresses,
+    pfs_mock,
 ):
     _, app1 = raiden_network
     token_address = token_addresses[0]
@@ -236,6 +257,7 @@ def test_api_payments_with_secret_no_hash(
     secret = to_hex(factories.make_secret())
 
     our_address = api_server_test_instance.rest_api.raiden_api.address
+    pfs_mock.add_apps(raiden_network)
 
     payment = {
         "initiator_address": to_checksum_address(our_address),
@@ -256,6 +278,7 @@ def test_api_payments_with_secret_no_hash(
     )
     with watch_for_unlock_failures(*raiden_network):
         response = request.send().response
+
     assert_proper_response(response)
     json_response = get_json_response(response)
     assert_payment_secret_and_hash(json_response, payment)
@@ -266,13 +289,15 @@ def test_api_payments_with_secret_no_hash(
 @pytest.mark.parametrize("number_of_nodes", [2])
 @pytest.mark.parametrize("enable_rest_api", [True])
 def test_api_payments_with_hash_no_secret(
-    api_server_test_instance, raiden_network: List[RaidenService], token_addresses
+    api_server_test_instance, raiden_network: List[RaidenService], token_addresses, pfs_mock
 ):
     _, app1 = raiden_network
     token_address = token_addresses[0]
     target_address = app1.address
     secret_hash = factories.make_secret_hash()
 
+    pfs_mock.add_apps(raiden_network)
+
     request = grequests.post(
         api_url_for(
             api_server_test_instance,
@@ -320,11 +345,12 @@ def test_api_payments_post_without_required_params(api_server_test_instance, tok
 @pytest.mark.parametrize("number_of_nodes", [2])
 @pytest.mark.parametrize("resolver_ports", [[None, 8000]])
 @pytest.mark.parametrize("enable_rest_api", [True])
+@pytest.mark.usefixtures("resolvers")
 def test_api_payments_with_resolver(
     api_server_test_instance: APIServer,
     raiden_network: List[RaidenService],
     token_addresses,
-    resolvers,  # pylint: disable=unused-argument
+    pfs_mock,
 ):
 
     _, app1 = raiden_network
@@ -334,6 +360,8 @@ def test_api_payments_with_resolver(
     target_address = app1.address
     secret_hash = factories.make_secret_hash()
 
+    pfs_mock.add_apps(raiden_network)
+
     # payment with secret_hash when both resolver and initiator don't have the secret
     request = grequests.post(
         api_url_for(
@@ -359,7 +387,11 @@ def test_api_payments_with_resolver(
             token_address=to_checksum_address(token_address),
             target_address=to_checksum_address(target_address),
         ),
-        json={"amount": str(amount), "identifier": str(identifier), "secret": to_hex(secret_hash)},
+        json={
+            "amount": str(amount),
+            "identifier": str(identifier),
+            "secret": to_hex(secret_hash),
+        },
     )
     response = request.send().response
     assert_proper_response(response, status_code=HTTPStatus.OK)
@@ -392,13 +424,18 @@ def test_api_payments_with_resolver(
 @pytest.mark.parametrize("number_of_nodes", [2])
 @pytest.mark.parametrize("enable_rest_api", [True])
 def test_api_payments_with_secret_and_hash(
-    api_server_test_instance: APIServer, raiden_network: List[RaidenService], token_addresses
+    api_server_test_instance: APIServer,
+    raiden_network: List[RaidenService],
+    token_addresses,
+    pfs_mock,
 ):
     _, app1 = raiden_network
     token_address = token_addresses[0]
     target_address = app1.address
     secret, secret_hash = factories.make_secret_with_hash()
 
+    pfs_mock.add_apps(raiden_network)
+
     our_address = api_server_test_instance.rest_api.raiden_api.address
 
     payment = {
@@ -436,12 +473,17 @@ def test_api_payments_with_secret_and_hash(
 @pytest.mark.parametrize("number_of_nodes", [2])
 @pytest.mark.parametrize("enable_rest_api", [True])
 def test_api_payments_conflicts(
-    api_server_test_instance: APIServer, raiden_network: List[RaidenService], token_addresses
+    api_server_test_instance: APIServer,
+    raiden_network: List[RaidenService],
+    token_addresses,
+    pfs_mock,
 ):
     _, app1 = raiden_network
     token_address = token_addresses[0]
     target_address = app1.address
 
+    pfs_mock.add_apps(raiden_network)
+
     payment_url = api_url_for(
         api_server_test_instance,
         "token_target_paymentresource",
@@ -474,7 +516,10 @@ def test_api_payments_conflicts(
 @pytest.mark.parametrize("enable_rest_api", [True])
 @pytest.mark.parametrize("deposit", [1000])
 def test_api_payments_with_lock_timeout(
-    api_server_test_instance: APIServer, raiden_network: List[RaidenService], token_addresses
+    api_server_test_instance: APIServer,
+    raiden_network: List[RaidenService],
+    token_addresses,
+    pfs_mock,
 ):
     _, app1 = raiden_network
     token_address = token_addresses[0]
@@ -483,6 +528,8 @@ def test_api_payments_with_lock_timeout(
     reveal_timeout = number_of_nodes * 4 + DEFAULT_NUMBER_OF_BLOCK_CONFIRMATIONS
     settle_timeout = 39
 
+    pfs_mock.add_apps(raiden_network)
+
     # try lock_timeout = reveal_timeout - should not work
     request = grequests.post(
         api_url_for(
@@ -557,13 +604,18 @@ def test_api_payments_with_lock_timeout(
 @pytest.mark.parametrize("number_of_nodes", [2])
 @pytest.mark.parametrize("enable_rest_api", [True])
 def test_api_payments_with_invalid_input(
-    api_server_test_instance: APIServer, raiden_network: List[RaidenService], token_addresses
+    api_server_test_instance: APIServer,
+    raiden_network: List[RaidenService],
+    token_addresses,
+    pfs_mock,
 ):
     _, app1 = raiden_network
     token_address = token_addresses[0]
     target_address = app1.address
     settle_timeout = 39
 
+    pfs_mock.add_apps(raiden_network)
+
     # Invalid identifier being 0 or negative
     request = grequests.post(
         api_url_for(
@@ -572,7 +624,11 @@ def test_api_payments_with_invalid_input(
             token_address=to_checksum_address(token_address),
             target_address=to_checksum_address(target_address),
         ),
-        json={"amount": DEFAULT_AMOUNT, "identifier": "0", "lock_timeout": str(settle_timeout)},
+        json={
+            "amount": DEFAULT_AMOUNT,
+            "identifier": "0",
+            "lock_timeout": str(settle_timeout),
+        },
     )
     response = request.send().response
     assert_response_with_error(response, status_code=HTTPStatus.CONFLICT)
@@ -584,7 +640,11 @@ def test_api_payments_with_invalid_input(
             token_address=to_checksum_address(token_address),
             target_address=to_checksum_address(target_address),
         ),
-        json={"amount": DEFAULT_AMOUNT, "identifier": "-1", "lock_timeout": str(settle_timeout)},
+        json={
+            "amount": DEFAULT_AMOUNT,
+            "identifier": "-1",
+            "lock_timeout": str(settle_timeout),
+        },
     )
     response = request.send().response
     assert_response_with_error(response, status_code=HTTPStatus.CONFLICT)
diff --git a/raiden/tests/integration/api/rest/test_rest.py b/raiden/tests/integration/api/rest/test_rest.py
index 75c7d61204..6f2832c8ea 100644
--- a/raiden/tests/integration/api/rest/test_rest.py
+++ b/raiden/tests/integration/api/rest/test_rest.py
@@ -74,7 +74,7 @@ class CustomException(Exception):
 @pytest.mark.parametrize("channels_per_node", [0])
 @pytest.mark.parametrize("enable_rest_api", [True])
 def test_payload_with_invalid_addresses(api_server_test_instance: APIServer):
-    """ Addresses require leading 0x in the payload. """
+    """Addresses require leading 0x in the payload."""
     invalid_address = "61c808d82a3ac53231750dadc13c777b59310bd9"
     channel_data_obj = {
         "partner_address": invalid_address,
@@ -107,7 +107,7 @@ def test_payload_with_invalid_addresses(api_server_test_instance: APIServer):
 @pytest.mark.parametrize("channels_per_node", [0])
 @pytest.mark.parametrize("enable_rest_api", [True])
 def test_crash_on_unhandled_exception(api_server_test_instance: APIServer) -> None:
-    """ Crash when an unhandled exception happens on APIServer. """
+    """Crash when an unhandled exception happens on APIServer."""
 
     # as we should not have unhandled exceptions in our endpoints, create one to test
     @api_server_test_instance.flask_app.route("/error_endpoint", methods=["GET"])
@@ -126,7 +126,7 @@ def error_endpoint():  # pylint: disable=unused-variable
 @pytest.mark.parametrize("channels_per_node", [0])
 @pytest.mark.parametrize("enable_rest_api", [True])
 def test_payload_with_address_invalid_chars(api_server_test_instance: APIServer):
-    """ Addresses cannot have invalid characters in it. """
+    """Addresses cannot have invalid characters in it."""
     invalid_address = "0x61c808d82a3ac53231750dadc13c777b59310bdg"  # g at the end is invalid
     channel_data_obj = {
         "partner_address": invalid_address,
@@ -145,7 +145,7 @@ def test_payload_with_address_invalid_chars(api_server_test_instance: APIServer)
 @pytest.mark.parametrize("channels_per_node", [0])
 @pytest.mark.parametrize("enable_rest_api", [True])
 def test_payload_with_address_invalid_length(api_server_test_instance: APIServer):
-    """ Encoded addresses must have the right length. """
+    """Encoded addresses must have the right length."""
     invalid_address = "0x61c808d82a3ac53231750dadc13c777b59310b"  # one char short
     channel_data_obj = {
         "partner_address": invalid_address,
@@ -164,7 +164,7 @@ def test_payload_with_address_invalid_length(api_server_test_instance: APIServer
 @pytest.mark.parametrize("channels_per_node", [0])
 @pytest.mark.parametrize("enable_rest_api", [True])
 def test_payload_with_address_not_eip55(api_server_test_instance: APIServer):
-    """ Provided addresses must be EIP55 encoded. """
+    """Provided addresses must be EIP55 encoded."""
     invalid_address = "0xf696209d2ca35e6c88e5b99b7cda3abf316bed69"
     channel_data_obj = {
         "partner_address": invalid_address,
@@ -399,7 +399,10 @@ def test_query_partners_by_token(
 @pytest.mark.parametrize("number_of_nodes", [2])
 @pytest.mark.parametrize("enable_rest_api", [True])
 def test_api_timestamp_format(
-    api_server_test_instance: APIServer, raiden_network: List[RaidenService], token_addresses
+    api_server_test_instance: APIServer,
+    raiden_network: List[RaidenService],
+    token_addresses,
+    pfs_mock,
 ) -> None:
     _, app1 = raiden_network
     amount = 200
@@ -407,6 +410,8 @@ def test_api_timestamp_format(
     token_address = token_addresses[0]
     target_address = app1.address
 
+    pfs_mock.add_apps(raiden_network)
+
     payment_url = api_url_for(
         api_server_test_instance,
         "token_target_paymentresource",
@@ -542,12 +547,16 @@ def test_get_connections_info(
 @pytest.mark.parametrize("enable_rest_api", [True])
 @pytest.mark.parametrize("number_of_tokens", [2])
 def test_payment_events_endpoints(
-    api_server_test_instance: APIServer, raiden_network: List[RaidenService], token_addresses
+    api_server_test_instance: APIServer,
+    raiden_network: List[RaidenService],
+    token_addresses,
+    pfs_mock,
 ):
     app0, app1, app2 = raiden_network
 
     token_address0 = token_addresses[0]
     token_address1 = token_addresses[1]
+    pfs_mock.add_apps(raiden_network)
 
     app0_server = api_server_test_instance
     app1_server = prepare_api_server(app1)
@@ -557,6 +566,7 @@ def test_payment_events_endpoints(
     identifier1 = PaymentID(10)
     amount1 = PaymentAmount(10)
     secret1, secrethash1 = factories.make_secret_with_hash()
+
     request = grequests.post(
         api_url_for(
             app0_server,
@@ -583,7 +593,7 @@ def test_payment_events_endpoints(
     )
     request.send()
 
-    # Payment 3: app0 is sending some tokens of token0 to app2
+    # Payment 3: app0 is sending some tokens of token0 to app1
     identifier3 = PaymentID(30)
     amount3 = PaymentAmount(17)
     secret3, secrethash3 = factories.make_secret_with_hash()
@@ -897,7 +907,10 @@ def test_payment_events_endpoints(
 @pytest.mark.parametrize("number_of_nodes", [2])
 @pytest.mark.parametrize("enable_rest_api", [True])
 def test_channel_events_raiden(
-    api_server_test_instance: APIServer, raiden_network: List[RaidenService], token_addresses
+    api_server_test_instance: APIServer,
+    raiden_network: List[RaidenService],
+    token_addresses,
+    pfs_mock,
 ):
     _, app1 = raiden_network
     amount = 100
@@ -905,6 +918,8 @@ def test_channel_events_raiden(
     token_address = token_addresses[0]
     target_address = app1.address
 
+    pfs_mock.add_apps(raiden_network)
+
     request = grequests.post(
         api_url_for(
             api_server_test_instance,
@@ -1208,7 +1223,7 @@ def test_no_udc_configured(api_server_test_instance: APIServer, retry_timeout):
 @pytest.mark.parametrize("channels_per_node", [0])
 @pytest.mark.parametrize("enable_rest_api", [True])
 def test_shutdown(api_server_test_instance: APIServer):
-    """ Node must stop after shutdown is called """
+    """Node must stop after shutdown is called"""
     url = ("http://localhost:{port}/api/v1/shutdown").format(
         port=api_server_test_instance.config.port
     )
diff --git a/raiden/tests/integration/api/test_pythonapi_regression.py b/raiden/tests/integration/api/test_pythonapi_regression.py
index 40a66de18b..dbb767e290 100644
--- a/raiden/tests/integration/api/test_pythonapi_regression.py
+++ b/raiden/tests/integration/api/test_pythonapi_regression.py
@@ -13,13 +13,15 @@
 @raise_on_failure
 @pytest.mark.parametrize("number_of_nodes", [2])
 @pytest.mark.parametrize("channels_per_node", [1])
-def test_close_regression(raiden_network: List[RaidenService], deposit, token_addresses):
+def test_close_regression(raiden_network: List[RaidenService], deposit, token_addresses, pfs_mock):
     """The python api was using the wrong balance proof to close the channel,
     thus the close was failing if a transfer was made.
     """
     app0, app1 = raiden_network
     token_address = token_addresses[0]
 
+    pfs_mock.add_apps(raiden_network)
+
     api1 = RaidenAPI(app0)
     api2 = RaidenAPI(app1)
 
diff --git a/raiden/tests/integration/cli/test_cli_development.py b/raiden/tests/integration/cli/test_cli_development.py
index 9824eaf504..39d7190d22 100644
--- a/raiden/tests/integration/cli/test_cli_development.py
+++ b/raiden/tests/integration/cli/test_cli_development.py
@@ -13,9 +13,6 @@
         "cli_tests_contracts_version", [RAIDEN_CONTRACT_VERSION], scope="module"
     ),
     pytest.mark.parametrize("environment_type", [Environment.DEVELOPMENT], scope="module"),
-    # This is a bit awkward, the default `chain_id` for the `raiden_testchain` and
-    # `local_matrix_servers` fixtures don't align. Therefore we force it to "smoketest" here.
-    pytest.mark.parametrize("chain_id", ["smoketest"]),
 ]
 
 
diff --git a/raiden/tests/integration/cli/test_cli_production.py b/raiden/tests/integration/cli/test_cli_production.py
index 27eb4fb128..81264f30ea 100644
--- a/raiden/tests/integration/cli/test_cli_production.py
+++ b/raiden/tests/integration/cli/test_cli_production.py
@@ -17,9 +17,6 @@
         "cli_tests_contracts_version", [RAIDEN_CONTRACT_VERSION], scope="module"
     ),
     pytest.mark.parametrize("environment_type", [EXPECTED_DEFAULT_ENVIRONMENT], scope="module"),
-    # This is a bit awkward, the default `chain_id` for the `raiden_testchain` and
-    # `local_matrix_servers` fixtures don't align. Therefore we force it to "smoketest" here.
-    pytest.mark.parametrize("chain_id", ["smoketest"]),
 ]
 
 
diff --git a/raiden/tests/integration/conftest.py b/raiden/tests/integration/conftest.py
index 149b2fcb07..3f23bef300 100644
--- a/raiden/tests/integration/conftest.py
+++ b/raiden/tests/integration/conftest.py
@@ -1,4 +1,5 @@
 import pytest
+
 from raiden.tests.integration.fixtures.blockchain import *  # noqa: F401,F403
 from raiden.tests.integration.fixtures.raiden_network import *  # noqa: F401,F403
 from raiden.tests.integration.fixtures.smartcontracts import *  # noqa: F401,F403
diff --git a/raiden/tests/integration/exception.py b/raiden/tests/integration/exception.py
index a06419eb21..0e6c209097 100644
--- a/raiden/tests/integration/exception.py
+++ b/raiden/tests/integration/exception.py
@@ -1,2 +1,2 @@
 class RetryTestError(Exception):
-    """ Use `flaky` to rerun a test failed due to this exception """
+    """Use `flaky` to rerun a test failed due to this exception"""
diff --git a/raiden/tests/integration/fixtures/blockchain.py b/raiden/tests/integration/fixtures/blockchain.py
index cd3ef1d9f0..3b3ea17217 100644
--- a/raiden/tests/integration/fixtures/blockchain.py
+++ b/raiden/tests/integration/fixtures/blockchain.py
@@ -34,7 +34,7 @@ def web3(
     logs_storage,
     blockchain_type,
 ):
-    """ Starts a private chain with accounts funded. """
+    """Starts a private chain with accounts funded."""
     # include the deploy key in the list of funded accounts
     keys_to_fund = sorted(set(private_keys + [deploy_key]))
 
diff --git a/raiden/tests/integration/fixtures/raiden_network.py b/raiden/tests/integration/fixtures/raiden_network.py
index 9b4a219de8..8d17b010c5 100644
--- a/raiden/tests/integration/fixtures/raiden_network.py
+++ b/raiden/tests/integration/fixtures/raiden_network.py
@@ -7,8 +7,11 @@
 from gevent.event import AsyncResult
 
 from raiden.constants import Environment, RoutingMode
+from raiden.network.pathfinding import PFSInfo
 from raiden.raiden_service import RaidenService
 from raiden.settings import CapabilitiesConfig
+from raiden.tests.utils import factories
+from raiden.tests.utils.mocks import PFSMock
 from raiden.tests.utils.network import (
     CHAIN,
     BlockchainServices,
@@ -23,6 +26,7 @@
 )
 from raiden.tests.utils.tests import shutdown_apps_and_cleanup_tasks
 from raiden.tests.utils.transport import ParsedURL
+from raiden.utils.formatting import to_canonical_address
 from raiden.utils.typing import (
     BlockNumber,
     BlockTimeout,
@@ -74,7 +78,6 @@ def raiden_chain(
     blockchain_type: str,
     contracts_path: Path,
     user_deposit_address: UserDepositAddress,
-    broadcast_rooms: List[str],
     logs_storage: str,
     register_tokens: bool,
     start_raiden_apps: bool,
@@ -118,7 +121,6 @@ def raiden_chain(
         unrecoverable_error_should_crash=unrecoverable_error_should_crash,
         local_matrix_url=local_matrix_servers[0],
         contracts_path=contracts_path,
-        broadcast_rooms=broadcast_rooms,
         routing_mode=routing_mode,
         blockchain_query_interval=blockchain_query_interval,
         resolver_ports=resolver_ports,
@@ -203,6 +205,56 @@ def capabilities(adhoc_capability) -> CapabilitiesConfig:
     return config
 
 
+@pytest.fixture
+def pfs_mock(
+    monkeypatch,
+    local_matrix_servers,
+    chain_id,
+    token_network_registry_address,
+    user_deposit_address,
+):
+
+    pfs_info = PFSInfo(
+        url="http://mock_pfs",
+        chain_id=chain_id,
+        token_network_registry_address=TokenNetworkRegistryAddress(
+            to_canonical_address(token_network_registry_address or factories.make_address())
+        ),
+        user_deposit_address=to_canonical_address(
+            user_deposit_address or factories.make_address()
+        ),
+        payment_address=to_canonical_address(factories.make_address()),
+        confirmed_block_number=BlockNumber(0),
+        message="",
+        operator="",
+        version="",
+        price=TokenAmount(0),
+        matrix_server=local_matrix_servers[0],
+    )
+
+    pfs_mock = PFSMock(pfs_info)
+
+    # NOTE: pfs_mock.add_apps() has to be called in the test in order to make the monkeypatched
+    # methods useful
+
+    # Patch the relevant functions in Raiden with the ones of the Mock:
+    # Methods used by initiator
+    monkeypatch.setattr("raiden.routing.query_address_metadata", pfs_mock.query_address_metadata)
+    monkeypatch.setattr("raiden.routing.get_best_routes_pfs", pfs_mock.get_best_routes_pfs)
+    # Method used by WithdrawRequest message handler
+    monkeypatch.setattr(
+        "raiden.message_handler.query_address_metadata", pfs_mock.query_address_metadata
+    )
+    # Method used by Withdraw in the API
+    monkeypatch.setattr(
+        "raiden.api.python.query_address_metadata", pfs_mock.query_address_metadata
+    )
+    # PFS info endpoint
+    monkeypatch.setattr("raiden.network.pathfinding", pfs_mock.get_pfs_info)
+
+    return pfs_mock
+
+
 @pytest.fixture
 def raiden_network(
     token_addresses: List[TokenAddress],
@@ -224,7 +276,6 @@ def raiden_network(
     blockchain_type: str,
     contracts_path: Path,
     user_deposit_address: Optional[UserDepositAddress],
-    broadcast_rooms: List[str],
     logs_storage: str,
     register_tokens: bool,
     start_raiden_apps: bool,
@@ -260,7 +311,6 @@ def raiden_network(
         environment_type=environment_type,
         unrecoverable_error_should_crash=unrecoverable_error_should_crash,
         local_matrix_url=local_matrix_servers[0],
-        broadcast_rooms=broadcast_rooms,
         routing_mode=routing_mode,
         blockchain_query_interval=blockchain_query_interval,
         resolver_ports=resolver_ports,
diff --git a/raiden/tests/integration/fixtures/smartcontracts.py b/raiden/tests/integration/fixtures/smartcontracts.py
index 6a46b7b7c8..c115def55d 100644
--- a/raiden/tests/integration/fixtures/smartcontracts.py
+++ b/raiden/tests/integration/fixtures/smartcontracts.py
@@ -504,7 +504,7 @@ def service_registry_address_fixture(
 def user_deposit_address_fixture(
     deploy_smart_contract_bundle_concurrently: FixtureSmartContracts,
 ) -> Optional[UserDepositAddress]:
-    """ Deploy UserDeposit and fund accounts with some balances """
+    """Deploy UserDeposit and fund accounts with some balances"""
     services_smart_contracts = deploy_smart_contract_bundle_concurrently.services_smart_contracts
 
     if services_smart_contracts:
@@ -517,7 +517,7 @@ def user_deposit_address_fixture(
 def one_to_n_address_fixture(
     deploy_smart_contract_bundle_concurrently: FixtureSmartContracts,
 ) -> Optional[OneToNAddress]:
-    """ Deploy OneToN contract and return the address """
+    """Deploy OneToN contract and return the address"""
     services_smart_contracts = deploy_smart_contract_bundle_concurrently.services_smart_contracts
 
     if services_smart_contracts:
@@ -530,7 +530,7 @@ def one_to_n_address_fixture(
 def monitoring_service_address_fixture(
     deploy_smart_contract_bundle_concurrently: FixtureSmartContracts,
 ) -> Optional[MonitoringServiceAddress]:
-    """ Deploy OneToN contract and return the address """
+    """Deploy OneToN contract and return the address"""
     services_smart_contracts = deploy_smart_contract_bundle_concurrently.services_smart_contracts
 
     if services_smart_contracts:
diff --git a/raiden/tests/integration/fixtures/transport.py b/raiden/tests/integration/fixtures/transport.py
index a9fa0d269e..0f62105933 100644
--- a/raiden/tests/integration/fixtures/transport.py
+++ b/raiden/tests/integration/fixtures/transport.py
@@ -2,9 +2,9 @@
 
 import pytest
 
-from raiden.constants import DISCOVERY_DEFAULT_ROOM, Environment
+from raiden.api.v1.encoding import CapabilitiesSchema
+from raiden.constants import Environment
 from raiden.network.transport import MatrixTransport
-from raiden.network.transport.matrix.utils import make_room_alias
 from raiden.settings import (
     DEFAULT_TRANSPORT_MATRIX_SYNC_TIMEOUT,
     CapabilitiesConfig,
@@ -14,14 +14,7 @@
 from raiden.tests.utils.transport import ParsedURL, generate_synapse_config, matrix_server_starter
 from raiden.utils.capabilities import capconfig_to_dict
 from raiden.utils.http import HTTPExecutor
-from raiden.utils.typing import (
-    AddressMetadata,
-    Iterable,
-    Optional,
-    PeerCapabilities,
-    Tuple,
-    UserID,
-)
+from raiden.utils.typing import AddressMetadata, Iterable, Optional, Tuple, UserID
 
 
 @pytest.fixture(scope="session")
@@ -37,7 +30,8 @@ def query_address_metadata(_pfs_config, user_address):
         return AddressMetadata(
             dict(
                 user_id=UserID(f"@0x{user_address.hex()}:{server_name}"),
-                capabilities=PeerCapabilities(capconfig_to_dict(capabilities)),
+                capabilities=CapabilitiesSchema().dump(capconfig_to_dict(capabilities)),
+                displayname="",  # FIXME: how to add correct displayname here?
             )
         )
 
@@ -61,20 +55,13 @@ def local_matrix_servers_with_executor(
     matrix_server_count,
     synapse_config_generator,
     port_generator,
-    broadcast_rooms,
-    chain_id,
 ) -> Iterable[List[Tuple[ParsedURL, HTTPExecutor]]]:
     if transport_protocol is not TransportProtocol.MATRIX:
         yield []
         return
 
-    broadcast_rooms_aliases = [
-        make_room_alias(chain_id, room_name) for room_name in broadcast_rooms
-    ]
-
     starter = matrix_server_starter(
         free_port_generator=port_generator,
-        broadcast_rooms_aliases=broadcast_rooms_aliases,
         count=matrix_server_count,
         config_generator=synapse_config_generator,
         log_context=request.node.name,
@@ -90,11 +77,6 @@ def local_matrix_servers(
     yield [url for url, _ in local_matrix_servers_with_executor]
 
 
-@pytest.fixture
-def broadcast_rooms() -> List[str]:
-    return [DISCOVERY_DEFAULT_ROOM]
-
-
 @pytest.fixture
 def matrix_transports(
     local_matrix_servers: List[ParsedURL],
@@ -102,7 +84,6 @@ def matrix_transports(
     retry_interval_initial: float,
     retry_interval_max: float,
     number_of_transports: int,
-    broadcast_rooms: List[str],
     matrix_sync_timeout: int,
     capabilities: CapabilitiesConfig,
     environment_type: Environment,
@@ -115,7 +96,6 @@ def matrix_transports(
         transports.append(
             MatrixTransport(
                 config=MatrixTransportConfig(
-                    broadcast_rooms=broadcast_rooms.copy(),
                     retries_before_backoff=retries_before_backoff,
                     retry_interval_initial=retry_interval_initial,
                     retry_interval_max=retry_interval_max,
diff --git a/raiden/tests/integration/long_running/test_integration_events.py b/raiden/tests/integration/long_running/test_integration_events.py
index 2e68d6b313..4c3b2c6810 100644
--- a/raiden/tests/integration/long_running/test_integration_events.py
+++ b/raiden/tests/integration/long_running/test_integration_events.py
@@ -411,7 +411,7 @@ def test_secret_revealed_on_chain(
     token_addresses,
     retry_interval_initial,
 ):
-    """ A node must reveal the secret on-chain if it's known and the channel is closed. """
+    """A node must reveal the secret on-chain if it's known and the channel is closed."""
     app0, app1, app2 = raiden_chain
     token_address = token_addresses[0]
     token_network_address = views.get_token_network_address_by_token_address(
@@ -515,7 +515,7 @@ def _mocked_unlock(self: TokenNetwork, *args, **kwargs):
 @raise_on_failure
 @pytest.mark.parametrize("number_of_nodes", [2])
 def test_clear_closed_queue(raiden_network: List[RaidenService], token_addresses, network_wait):
-    """ Closing a channel clears the respective message queue. """
+    """Closing a channel clears the respective message queue."""
     app0, app1 = raiden_network
 
     hold_event_handler = app1.raiden_event_handler
diff --git a/raiden/tests/integration/long_running/test_settlement.py b/raiden/tests/integration/long_running/test_settlement.py
index 09eb5da495..5bb71e1d65 100644
--- a/raiden/tests/integration/long_running/test_settlement.py
+++ b/raiden/tests/integration/long_running/test_settlement.py
@@ -247,6 +247,7 @@ def test_lock_expiry(
         target=target,
         identifier=identifier,
         secret=transfer_1_secret,
+        route_states=[create_route_state_for_route([alice_app, bob_app], token_address)],
     )
     transfer1_received.wait()
 
@@ -297,6 +298,7 @@ def test_lock_expiry(
         target=target,
         identifier=identifier,
         secret=transfer_2_secret,
+        route_states=[create_route_state_for_route([alice_app, bob_app], token_address)],
     )
     transfer2_received.wait()
 
@@ -360,6 +362,7 @@ def test_batch_unlock(
         target=TargetAddress(bob_address),
         identifier=PaymentID(identifier),
         secret=secret,
+        route_states=[create_route_state_for_route([alice_app, bob_app], token_address)],
     )
 
     secret_request_event.get()  # wait for the messages to be exchanged
@@ -478,9 +481,13 @@ def test_channel_withdraw(
     token_addresses: List[TokenAddress],
     deposit: TokenAmount,
     retry_timeout: float,
+    pfs_mock,
 ) -> None:
-    """ Withdraw funds after a mediated transfer."""
+    """Withdraw funds after a mediated transfer."""
     alice_app, bob_app = raiden_network
+
+    pfs_mock.add_apps(raiden_network)
+
     token_address = token_addresses[0]
     token_network_address = views.get_token_network_address_by_token_address(
         views.state_from_raiden(alice_app),
@@ -522,9 +529,12 @@ def test_channel_withdraw(
     total_withdraw = WithdrawAmount(deposit + alice_to_bob_amount)
 
     bob_alice_channel_state = get_channelstate(bob_app, alice_app, token_network_address)
+
+    alice_metadata = pfs_mock.query_address_metadata(bob_app.config.pfs_config, alice_app.address)
     bob_app.withdraw(
         canonical_identifier=bob_alice_channel_state.canonical_identifier,
         total_withdraw=total_withdraw,
+        recipient_metadata=alice_metadata,
     )
 
     waiting.wait_for_withdraw_complete(
@@ -547,9 +557,12 @@ def test_channel_withdraw_expired(
     token_addresses: List[TokenAddress],
     deposit: TokenAmount,
     retry_timeout: float,
+    pfs_mock,
 ) -> None:
-    """ Tests withdraw expiration. """
+    """Tests withdraw expiration."""
     alice_app, bob_app = raiden_network
+    pfs_mock.add_apps(raiden_network)
+
     token_address = token_addresses[0]
     token_network_address = views.get_token_network_address_by_token_address(
         views.state_from_raiden(alice_app),
@@ -587,6 +600,7 @@ def test_channel_withdraw_expired(
         target=target,
         identifier=identifier,
         secret=secret,
+        route_states=[create_route_state_for_route([alice_app, bob_app], token_address)],
     )
     wait_for_unlock = bob_app.message_handler.wait_for_message(
         Unlock, {"payment_identifier": identifier}
@@ -601,9 +615,11 @@ def test_channel_withdraw_expired(
 
     bob_alice_channel_state = get_channelstate(bob_app, alice_app, token_network_address)
 
+    alice_metadata = pfs_mock.query_address_metadata(bob_app.config.pfs_config, alice_app.address)
     bob_app.withdraw(
         canonical_identifier=bob_alice_channel_state.canonical_identifier,
         total_withdraw=total_withdraw,
+        recipient_metadata=alice_metadata,
     )
 
     with block_offset_timeout(bob_app):
@@ -706,6 +722,7 @@ def test_settled_lock(
         token_address=token_address,
         amount=amount,
         identifier=PaymentID(2),
+        routes=[[app0, app1]],
     )
 
     # The channel state has to be recovered before the settlement, otherwise
@@ -785,6 +802,7 @@ def test_automatic_secret_registration(
         target=target,
         identifier=identifier,
         secret=secret,
+        route_states=[create_route_state_for_route([app0, app1], token_address)],
     )
 
     # Wait for app1 to receive the locked transfer.
@@ -834,10 +852,10 @@ def test_start_end_attack(
     The intention is to make the hub transfer the token but for him to be
     unable to require the token A1."""
 
-    token = token_addresses[0]
+    token_address = token_addresses[0]
     app0, app1, app2 = raiden_chain  # pylint: disable=unbalanced-tuple-unpacking
     token_network_address = views.get_token_network_address_by_token_address(
-        views.state_from_raiden(app0), app0.default_registry.address, token
+        views.state_from_raiden(app0), app0.default_registry.address, token_address
     )
     assert token_network_address
 
@@ -859,6 +877,7 @@ def test_start_end_attack(
         target=target,
         identifier=identifier,
         secret=secret,
+        route_states=[create_route_state_for_route([app0, app1], token_address)],
     )
 
     attack_channel = get_channelstate(app2, app1, token_network_address)
@@ -890,15 +909,15 @@ def test_start_end_attack(
     # the attacker settles the contract
     app2.rpc_client.wait_until_block(target_block_number=app2.rpc_client.block_number() + 1)
 
-    attack_channel.netting_channel.settle(token, attack_contract)
+    attack_channel.netting_channel.settle(token_address, attack_contract)
 
     # at this point the attacker has the "stolen" funds
-    attack_contract = app2.proxy_manager.token_hashchannel[token][attack_contract]
+    attack_contract = app2.proxy_manager.token_hashchannel[token_address][attack_contract]
     assert attack_contract.participants[app2.address]["netted"] == deposit + amount
     assert attack_contract.participants[app1.address]["netted"] == deposit - amount
 
     # and the hub's channel A1-H doesn't
-    hub_contract = app1.proxy_manager.token_hashchannel[token][hub_contract]
+    hub_contract = app1.proxy_manager.token_hashchannel[token_address][hub_contract]
     assert hub_contract.participants[app0.address]["netted"] == deposit
     assert hub_contract.participants[app1.address]["netted"] == deposit
 
@@ -911,7 +930,7 @@ def test_start_end_attack(
     # XXX: verify that the Hub has found the secret, close and settle the channel
 
     # the hub has acquired its token
-    hub_contract = app1.proxy_manager.token_hashchannel[token][hub_contract]
+    hub_contract = app1.proxy_manager.token_hashchannel[token_address][hub_contract]
     assert hub_contract.participants[app0.address]["netted"] == deposit + amount
     assert hub_contract.participants[app1.address]["netted"] == deposit - amount
 
@@ -941,6 +960,7 @@ def test_automatic_dispute(
         token_address=token_address,
         amount=amount0_1,
         identifier=PaymentID(1),
+        routes=[[app0, app1]],
     )
 
     amount1_1 = PaymentAmount(50)
@@ -950,6 +970,7 @@ def test_automatic_dispute(
         token_address=token_address,
         amount=amount1_1,
         identifier=PaymentID(2),
+        routes=[[app1, app0]],
     )
 
     amount0_2 = PaymentAmount(60)
@@ -959,6 +980,7 @@ def test_automatic_dispute(
         token_address=token_address,
         amount=amount0_2,
         identifier=PaymentID(3),
+        routes=[[app0, app1]],
     )
 
     # Alice can only provide one of Bob's transfer, so she is incentivized to
@@ -1061,6 +1083,7 @@ def test_batch_unlock_after_restart(
         target=TargetAddress(bob_app.address),
         identifier=identifier,
         secret=alice_transfer_secret,
+        route_states=[create_route_state_for_route([alice_app, bob_app], token_address)],
     )
 
     bob_app.mediated_transfer_async(
@@ -1069,6 +1092,7 @@ def test_batch_unlock_after_restart(
         target=TargetAddress(alice_app.address),
         identifier=PaymentID(identifier + 1),
         secret=bob_transfer_secret,
+        route_states=[create_route_state_for_route([bob_app, alice_app], token_address)],
     )
 
     alice_transfer_hold.wait(timeout=timeout)
@@ -1181,6 +1205,7 @@ def test_handle_insufficient_eth(
             token_address=token,
             amount=PaymentAmount(1),
             identifier=PaymentID(1),
+            routes=[[app0, app1]],
         )
 
     app1.stop()
diff --git a/raiden/tests/integration/long_running/test_stress.py b/raiden/tests/integration/long_running/test_stress.py
index 09746329a5..5df1af1171 100644
--- a/raiden/tests/integration/long_running/test_stress.py
+++ b/raiden/tests/integration/long_running/test_stress.py
@@ -1,6 +1,6 @@
 import time
 from http import HTTPStatus
-from itertools import combinations, count
+from itertools import count
 from typing import Sequence
 
 import gevent
@@ -10,7 +10,6 @@
 from eth_utils import to_canonical_address
 from flask import url_for
 
-from raiden import waiting
 from raiden.api.python import RaidenAPI
 from raiden.api.rest import APIServer, RestAPI
 from raiden.constants import RoutingMode
@@ -118,7 +117,7 @@ def restart_app(app: RaidenService, restart_node: RestartNode) -> RaidenService:
 
 
 def restart_network(
-    raiden_network: List[RaidenService], restart_node: RestartNode, retry_timeout: float
+    raiden_network: List[RaidenService], restart_node: RestartNode
 ) -> List[RaidenService]:
     for app in raiden_network:
         app.stop()
@@ -129,10 +128,6 @@ def restart_network(
 
     new_network = [greenlet.get() for greenlet in wait_network]
 
-    # The tests assume the nodes are available to transfer
-    for app0, app1 in combinations(new_network, 2):
-        waiting.wait_for_healthy(app0, app1.address, retry_timeout)
-
     return new_network
 
 
@@ -141,13 +136,12 @@ def restart_network_and_apiservers(
     restart_node: RestartNode,
     api_servers: List[APIServer],
     port_generator: Iterator[Port],
-    retry_timeout: float,
 ) -> Tuple[List[RaidenService], List[APIServer]]:
     """Stop an app and start it back"""
     for rest_api in api_servers:
         rest_api.stop()
 
-    new_network = restart_network(raiden_network, restart_node, retry_timeout)
+    new_network = restart_network(raiden_network, restart_node)
     new_servers = start_apiserver_for_network(new_network, port_generator)
 
     return (new_network, new_servers)
@@ -370,7 +364,6 @@ def test_stress(
     raiden_network: List[RaidenService],
     restart_node: RestartNode,
     deposit: TokenAmount,
-    retry_timeout: float,
     token_addresses: List[TokenAddress],
     port_generator: Iterator[Port],
 ) -> None:
@@ -392,7 +385,7 @@ def test_stress(
             stress_send_serial_transfers(rest_apis, token_address, identifier_generator, deposit)
 
         raiden_network, rest_apis = restart_network_and_apiservers(
-            raiden_network, restart_node, rest_apis, port_generator, retry_timeout
+            raiden_network, restart_node, rest_apis, port_generator
         )
 
         assert_channels(raiden_network, token_network_address, deposit)
@@ -401,7 +394,7 @@ def test_stress(
             stress_send_parallel_transfers(rest_apis, token_address, identifier_generator, deposit)
 
         raiden_network, rest_apis = restart_network_and_apiservers(
-            raiden_network, restart_node, rest_apis, port_generator, retry_timeout
+            raiden_network, restart_node, rest_apis, port_generator
         )
 
         assert_channels(raiden_network, token_network_address, deposit)
@@ -412,7 +405,7 @@ def test_stress(
             )
 
         raiden_network, rest_apis = restart_network_and_apiservers(
-            raiden_network, restart_node, rest_apis, port_generator, retry_timeout
+            raiden_network, restart_node, rest_apis, port_generator
         )
 
-    restart_network(raiden_network, restart_node, retry_timeout)
+    restart_network(raiden_network, restart_node)
diff --git a/raiden/tests/integration/network/proxies/test_token_network_registry.py b/raiden/tests/integration/network/proxies/test_token_network_registry.py
index a669c75862..106fe7f030 100644
--- a/raiden/tests/integration/network/proxies/test_token_network_registry.py
+++ b/raiden/tests/integration/network/proxies/test_token_network_registry.py
@@ -152,7 +152,7 @@ def test_token_network_registry(
 def test_token_network_registry_max_token_networks(
     deploy_client, token_network_registry_address, contract_manager
 ):
-    """ get_max_token_networks() should return an integer """
+    """get_max_token_networks() should return an integer"""
     proxy_manager = ProxyManager(
         rpc_client=deploy_client,
         contract_manager=contract_manager,
@@ -175,7 +175,7 @@ def test_token_network_registry_max_token_networks(
 def test_token_network_registry_with_zero_token_address(
     deploy_client, token_network_registry_address, contract_manager
 ):
-    """ Try to register a token at 0x0000..00 """
+    """Try to register a token at 0x0000..00"""
     proxy_manager = ProxyManager(
         rpc_client=deploy_client,
         contract_manager=contract_manager,
diff --git a/raiden/tests/integration/network/transport/test_matrix_transport.py b/raiden/tests/integration/network/transport/test_matrix_transport.py
index ff8e57055c..9e6939ead1 100644
--- a/raiden/tests/integration/network/transport/test_matrix_transport.py
+++ b/raiden/tests/integration/network/transport/test_matrix_transport.py
@@ -1,6 +1,4 @@
 import random  # pylint: skip-file  XXX-UAM remove after tests are updated
-from functools import partial
-from typing import Any
 from unittest.mock import MagicMock, Mock
 
 import gevent
@@ -9,31 +7,17 @@
 from gevent import Timeout
 
 import raiden
-from raiden.constants import (
-    BLOCK_ID_LATEST,
-    DISCOVERY_DEFAULT_ROOM,
-    EMPTY_SIGNATURE,
-    DeviceIDs,
-    Environment,
-    RoutingMode,
-)
-from raiden.exceptions import InsufficientEth
+from raiden.constants import BLOCK_ID_LATEST, EMPTY_SIGNATURE, DeviceIDs, Environment, RoutingMode
 from raiden.messages.monitoring_service import RequestMonitoring
 from raiden.messages.path_finding_service import PFSCapacityUpdate, PFSFeeUpdate
 from raiden.messages.synchronization import Delivered, Processed
-from raiden.network.transport.matrix.client import Room
 from raiden.network.transport.matrix.transport import (
     MatrixTransport,
     MessagesQueue,
     _RetryQueue,
     populate_services_addresses,
 )
-from raiden.network.transport.matrix.utils import (
-    AddressReachability,
-    UserPresence,
-    make_room_alias,
-)
-from raiden.raiden_service import RaidenService
+from raiden.network.transport.matrix.utils import AddressReachability
 from raiden.services import send_pfs_update, update_monitoring_service_from_balance_proof
 from raiden.settings import (
     MIN_MONITORING_AMOUNT_DAI,
@@ -45,8 +29,6 @@
 )
 from raiden.storage.serialization.serializer import MessageSerializer
 from raiden.tests.utils import factories
-from raiden.tests.utils.client import burn_eth
-from raiden.tests.utils.detect_failure import expect_failure, raise_on_failure
 from raiden.tests.utils.factories import (
     HOP1,
     CanonicalIdentifierProperties,
@@ -55,15 +37,11 @@
 )
 from raiden.tests.utils.mocks import MockRaidenService
 from raiden.tests.utils.smartcontracts import deploy_service_registry_and_set_urls
-from raiden.tests.utils.transfer import wait_assert
 from raiden.transfer import views
 from raiden.transfer.identifiers import CANONICAL_IDENTIFIER_UNORDERED_QUEUE, QueueIdentifier
-from raiden.transfer.state import NetworkState
-from raiden.transfer.state_change import ActionChannelClose
-from raiden.utils.formatting import to_checksum_address
 from raiden.utils.keys import privatekey_to_address
-from raiden.utils.typing import Address, Dict, List
-from raiden.waiting import wait_for_network_state
+from raiden.utils.typing import Address, MessageID
+from raiden_contracts.utils.type_aliases import ChainID
 
 HOP1_BALANCE_PROOF = factories.BalanceProofSignedStateProperties(pkey=factories.HOP1_KEY)
 
@@ -147,14 +125,16 @@ def ping_pong_message_success(transport0, transport1):
 
     msg_id = random.randint(1e5, 9e5)
 
-    ping_message = Processed(message_identifier=msg_id, signature=EMPTY_SIGNATURE)
-    pong_message = Delivered(delivered_message_identifier=msg_id, signature=EMPTY_SIGNATURE)
+    ping_message = Processed(message_identifier=MessageID(msg_id), signature=EMPTY_SIGNATURE)
+    pong_message = Delivered(
+        delivered_message_identifier=MessageID(msg_id), signature=EMPTY_SIGNATURE
+    )
 
     transport0_raiden_queues[queueid1].append(ping_message)
 
     transport0._raiden_service.sign(ping_message)
     transport1._raiden_service.sign(pong_message)
-    transport0.send_async([MessagesQueue(queueid1, [ping_message])])
+    transport0.send_async([MessagesQueue(queueid1, [(ping_message, transport1.address_metadata)])])
 
     with Timeout(TIMEOUT_MESSAGE_RECEIVE, exception=False):
         all_messages_received = False
@@ -171,7 +151,7 @@ def ping_pong_message_success(transport0, transport1):
 
     transport0._raiden_service.sign(pong_message)
     transport1._raiden_service.sign(ping_message)
-    transport1.send_async([MessagesQueue(queueid0, [ping_message])])
+    transport1.send_async([MessagesQueue(queueid0, [(ping_message, transport0.address_metadata)])])
 
     with Timeout(TIMEOUT_MESSAGE_RECEIVE, exception=False):
         all_messages_received = False
@@ -232,7 +212,6 @@ def wait_for_peer_reachable(transport: MatrixTransport, target_address: Address,
     )
 
 
-@pytest.mark.skip(reason="Test is still using presence / health check")
 @pytest.mark.parametrize("matrix_server_count", [2])
 @pytest.mark.parametrize("number_of_transports", [2])
 def test_matrix_message_sync(matrix_transports):
@@ -253,10 +232,6 @@ def test_matrix_message_sync(matrix_transports):
     transport0.start(raiden_service0, None)
     transport1.start(raiden_service1, None)
 
-    # XXX-UAM here was the health check before
-    # transport0.immediate_health_check_for(transport1._raiden_service.address)
-    # transport1.immediate_health_check_for(transport0._raiden_service.address)
-
     queue_identifier = QueueIdentifier(
         recipient=transport1._raiden_service.address,
         canonical_identifier=factories.UNIT_CANONICAL_ID,
@@ -266,10 +241,12 @@ def test_matrix_message_sync(matrix_transports):
     raiden0_queues[queue_identifier] = []
 
     for i in range(5):
-        message = Processed(message_identifier=i, signature=EMPTY_SIGNATURE)
+        message = Processed(message_identifier=MessageID(i), signature=EMPTY_SIGNATURE)
         raiden0_queues[queue_identifier].append(message)
         transport0._raiden_service.sign(message)
-        transport0.send_async([MessagesQueue(queue_identifier, [message])])
+        transport0.send_async(
+            [MessagesQueue(queue_identifier, [(message, transport1.address_metadata)])]
+        )
 
     with Timeout(TIMEOUT_MESSAGE_RECEIVE):
         while not len(transport0_messages) == 5:
@@ -291,19 +268,17 @@ def test_matrix_message_sync(matrix_transports):
 
     transport1.stop()
 
-    wait_for_peer_unreachable(transport0, transport1._raiden_service.address)
-
     # Send more messages while the other end is offline
     for i in range(10, 15):
-        message = Processed(message_identifier=i, signature=EMPTY_SIGNATURE)
+        message = Processed(message_identifier=MessageID(i), signature=EMPTY_SIGNATURE)
         raiden0_queues[queue_identifier].append(message)
         transport0._raiden_service.sign(message)
-        transport0.send_async([MessagesQueue(queue_identifier, [message])])
+        transport0.send_async(
+            [MessagesQueue(queue_identifier, [(message, transport1.address_metadata)])]
+        )
 
     # Should fetch the 5 messages sent while transport1 was offline
     transport1.start(transport1._raiden_service, None)
-    # XXX-UAM here was the health check before
-    # transport1.immediate_health_check_for(transport0._raiden_service.address)
 
     with gevent.Timeout(TIMEOUT_MESSAGE_RECEIVE):
         while len(transport1_messages) != 10:
@@ -321,62 +296,22 @@ def test_matrix_message_sync(matrix_transports):
         assert any(m.delivered_message_identifier == i for m in transport0_messages)
 
 
-@expect_failure
-@pytest.mark.skip(reason="Test is still using presence / health check")
-@pytest.mark.parametrize("number_of_nodes", [2])
-@pytest.mark.parametrize("channels_per_node", [1])
-@pytest.mark.parametrize("number_of_tokens", [1])
-def test_matrix_tx_error_handling(  # pylint: disable=unused-argument
-    raiden_chain: List[RaidenService], token_addresses, request
-):
-    """Proxies exceptions must be forwarded by the transport."""
-    if request.config.option.usepdb:
-        pytest.skip("test fails with pdb")
-    app0, app1 = raiden_chain
-    token_address = token_addresses[0]
-
-    channel_state = views.get_channelstate_for(
-        chain_state=views.state_from_raiden(app0),
-        token_network_registry_address=app0.default_registry.address,
-        token_address=token_address,
-        partner_address=app1.address,
-    )
-    burn_eth(app0.rpc_client)
-
-    def make_tx(*args, **kwargs):  # pylint: disable=unused-argument
-        close_channel = ActionChannelClose(canonical_identifier=channel_state.canonical_identifier)
-        app0.handle_and_track_state_changes([close_channel])
-
-    app0.transport._client.add_presence_listener(make_tx)
-
-    exception = ValueError("Exception was not raised from the transport")
-    with pytest.raises(InsufficientEth), gevent.Timeout(10, exception=exception):
-        # Change presence in peer app to trigger callback in app0
-        app1.transport._client.set_presence_state(UserPresence.UNAVAILABLE.value)
-        app0.greenlet.get()
-
-
-@pytest.mark.skip(reason="Test is still using presence / health check")
 def test_matrix_message_retry(
     local_matrix_servers,
     retry_interval_initial,
     retry_interval_max,
     retries_before_backoff,
-    broadcast_rooms,
 ):
     """Test the retry mechanism implemented into the matrix client.
-    The test creates a transport and sends a message. Given that the
-    receiver was online, the initial message is sent but the receiver
-    doesn't respond in time and goes offline. The retrier should then
+    The test creates a transport and sends a message. The receiver
+    doesn't respond in time. The retrier should then
     wait for the `retry_interval` duration to pass and send the message
-    again but this won't work because the receiver is offline. Once
-    the receiver comes back again, the message should be sent again.
+    again.
     """
     partner_address = factories.make_address()
 
     transport = MatrixTransport(
         config=MatrixTransportConfig(
-            broadcast_rooms=broadcast_rooms,
             retries_before_backoff=retries_before_backoff,
             retry_interval_initial=retry_interval_initial,
             retry_interval_max=retry_interval_max,
@@ -391,12 +326,6 @@ def test_matrix_message_retry(
     transport.start(raiden_service, None)
     transport.log = MagicMock()
 
-    # Receiver is online
-    # XXX-UAM uam was accessed here
-    # transport._address_mgr._address_to_reachabilitystate[partner_address] = ReachabilityState(
-    #     AddressReachability.REACHABLE, datetime.now()
-    # )
-
     queueid = QueueIdentifier(
         recipient=partner_address, canonical_identifier=CANONICAL_IDENTIFIER_UNORDERED_QUEUE
     )
@@ -406,7 +335,7 @@ def test_matrix_message_retry(
     assert bool(retry_queue), "retry_queue not running"
 
     # Send the initial message
-    message = Processed(message_identifier=0, signature=EMPTY_SIGNATURE)
+    message = Processed(message_identifier=MessageID(0), signature=EMPTY_SIGNATURE)
     transport._raiden_service.sign(message)
     chain_state.queueids_to_queues[queueid] = [message]
     retry_queue.enqueue_unordered(message)
@@ -414,29 +343,6 @@ def test_matrix_message_retry(
     gevent.idle()
     assert transport._send_raw.call_count == 1
 
-    # Receiver goes offline
-    # XXX-UAM uam was accessed here
-    # transport._address_mgr._address_to_reachabilitystate[partner_address] = ReachabilityState(
-    #     AddressReachability.UNREACHABLE, datetime.now()
-    # )
-
-    with gevent.Timeout(retry_interval_initial + 2):
-        wait_assert(
-            transport.log.debug.assert_called_with,
-            "Partner not reachable. Skipping.",
-            partner=to_checksum_address(partner_address),
-            status=AddressReachability.UNREACHABLE,
-        )
-
-    # Retrier did not call send_raw given that the receiver is still offline
-    assert transport._send_raw.call_count == 1
-
-    # Receiver comes back online
-    # XXX-UAM uam was accessed here
-    # transport._address_mgr._address_to_reachabilitystate[partner_address] = ReachabilityState(
-    #     AddressReachability.REACHABLE, datetime.now()
-    # )
-
     # Retrier should send the message again
     with gevent.Timeout(retry_interval_initial + 2):
         while transport._send_raw.call_count != 2:
@@ -447,10 +353,10 @@ def test_matrix_message_retry(
 
 
 @pytest.mark.parametrize("matrix_server_count", [3])
-@pytest.mark.parametrize("number_of_transports", [3])
+@pytest.mark.parametrize("number_of_transports", [2])
 def test_matrix_transport_handles_metadata(matrix_transports):
 
-    transport0, transport1, transport2 = matrix_transports
+    transport0, transport1 = matrix_transports
 
     transport0_messages = set()
     transport1_messages = set()
@@ -460,13 +366,11 @@ def test_matrix_transport_handles_metadata(matrix_transports):
 
     raiden_service0 = MockRaidenService(transport0_message_handler)
     raiden_service1 = MockRaidenService(transport1_message_handler)
-    raiden_service2 = MockRaidenService()
 
     raiden_service1.handle_and_track_state_changes = MagicMock()
 
     transport0.start(raiden_service0, None)
     transport1.start(raiden_service1, None)
-    transport2.start(raiden_service2, None)
 
     queue_identifier = QueueIdentifier(
         recipient=transport1._raiden_service.address,
@@ -476,90 +380,48 @@ def test_matrix_transport_handles_metadata(matrix_transports):
     raiden0_queues = views.get_all_messagequeues(views.state_from_raiden(raiden_service0))
     raiden0_queues[queue_identifier] = []
 
-    correct_metadata = {"user_id": transport1.user_id}
-    # This is the wrong user for the chosen address (address is implicit by the queue_identifier)
-    incorrect_metadata = {"user_id": transport2.user_id}
-    # invalid metadata, will lead to the fallback user-id generation
-    invalid_metadata = {"user_id": "invalid"}
-    no_metadata = None
+    correct_metadata = {"user_id": transport1.user_id}  # should correctly arrive at receiver
+    invalid_metadata = {"user_id": "invalid"}  # shouldn't arrive at receiver
+    no_metadata = None  # shouldn't arrive at receiver
 
-    all_metadata = (correct_metadata, incorrect_metadata, invalid_metadata, no_metadata)
+    all_metadata = (correct_metadata, invalid_metadata, no_metadata)
     num_sends = 2
     message_id = 0
 
-    for _ in range(num_sends):
-        for metadata in all_metadata:
+    for metadata in all_metadata:
+        for _ in range(num_sends):
             message = Processed(message_identifier=message_id, signature=EMPTY_SIGNATURE)
             raiden0_queues[queue_identifier].append(message)
+
             transport0._raiden_service.sign(message)
             message_queues = [MessagesQueue(queue_identifier, [(message, metadata)])]
             transport0.send_async(message_queues)
             message_id += 1
 
-    num_expected_messages = 3 * num_sends
+    num_expected_messages = num_sends
     with Timeout(TIMEOUT_MESSAGE_RECEIVE):
+        # Delivered messages
         while len(transport0_messages) < num_expected_messages:
             gevent.sleep(0.1)
 
+        # Processed messages
         while len(transport1_messages) < num_expected_messages:
             gevent.sleep(0.1)
 
-    # TODO also track / assert the number of to-device messages
-    #  and account for fallback user-ids and wrong user-ids!!
-    #  Because we have multiple messages per user-id (num_sends),
-    #  the calls to-device should be reduced per user-id because of batching
-
     # transport1 receives the `Processed` messages sent by transport0
-    for i in range(num_expected_messages):
-        assert any(m.message_identifier == i for m in transport1_messages)
+    ids = [m.message_identifier for m in transport1_messages]
+    # transport0 receives the `Delivered` messages sent by transport1
+    delivered_ids = [m.delivered_message_identifier for m in transport0_messages]
 
-    # transport0 answers with a `Delivered` for each `Processed`
-    for i in range(num_expected_messages):
-        assert any(m.delivered_message_identifier == i for m in transport0_messages)
+    assert sorted(ids) == sorted(delivered_ids)
+    assert sorted(ids) == list(range(0, num_sends))
+    assert len(transport0_messages) == num_expected_messages
+    assert len(transport1_messages) == num_expected_messages
 
     transport0.stop()
     transport1.stop()
-    transport2.stop()
 
 
-def test_join_invalid_discovery(
-    local_matrix_servers,
-    retry_interval_initial,
-    retry_interval_max,
-    retries_before_backoff,
-    broadcast_rooms,
-):
-    """join_broadcast_room tries to join on all servers on available_servers config
-
-    If any of the servers isn't reachable by synapse, it'll return a 500 response, which needs
-    to be handled, and if no discovery room is found on any of the available_servers, one in
-    our current server should be created
-    """
-    transport = MatrixTransport(
-        config=MatrixTransportConfig(
-            broadcast_rooms=broadcast_rooms,
-            retries_before_backoff=retries_before_backoff,
-            retry_interval_initial=retry_interval_initial,
-            retry_interval_max=retry_interval_max,
-            server=local_matrix_servers[0],
-            available_servers=["http://invalid.server"],
-        ),
-        environment=Environment.DEVELOPMENT,
-    )
-    transport._client.api.retry_timeout = 0
-    transport._send_raw = MagicMock()
-    raiden_service = MockRaidenService(None)
-
-    transport.start(raiden_service, None)
-    transport.log = MagicMock()
-    discovery_room_name = make_room_alias(transport.chain_id, "discovery")
-    assert isinstance(transport.broadcast_rooms.get(discovery_room_name), Room)
-
-    transport.stop()
-    transport.greenlet.get()
-
-
-@pytest.mark.skip(reason="Test is still using presence / health check")
 @pytest.mark.parametrize("matrix_server_count", [2])
 @pytest.mark.parametrize("number_of_transports", [3])
 def test_matrix_cross_server_with_load_balance(matrix_transports):
@@ -580,16 +442,6 @@ def test_matrix_cross_server_with_load_balance(matrix_transports):
     transport1.start(raiden_service1, "")
     transport2.start(raiden_service2, "")
 
-    # XXX-UAM here was the health check before
-    # transport0.immediate_health_check_for(raiden_service1.address)
-    # transport0.immediate_health_check_for(raiden_service2.address)
-    #
-    # transport1.immediate_health_check_for(raiden_service0.address)
-    # transport1.immediate_health_check_for(raiden_service2.address)
-
-    # transport2.immediate_health_check_for(raiden_service0.address)
-    # transport2.immediate_health_check_for(raiden_service1.address)
-
     assert ping_pong_message_success(transport0, transport1)
     assert ping_pong_message_success(transport0, transport2)
     assert ping_pong_message_success(transport1, transport0)
@@ -598,36 +450,6 @@ def test_matrix_cross_server_with_load_balance(matrix_transports):
     assert ping_pong_message_success(transport2, transport1)
 
 
-def test_matrix_discovery_room_offline_server(
-    local_matrix_servers,
-    retries_before_backoff,
-    retry_interval_initial,
-    retry_interval_max,
-    broadcast_rooms,
-):
-
-    transport = MatrixTransport(
-        config=MatrixTransportConfig(
-            broadcast_rooms=broadcast_rooms,
-            retries_before_backoff=retries_before_backoff,
-            retry_interval_initial=retry_interval_initial,
-            retry_interval_max=retry_interval_max,
-            server=local_matrix_servers[0],
-            available_servers=[local_matrix_servers[0], "https://localhost:1"],
-        ),
-        environment=Environment.DEVELOPMENT,
-    )
-    transport.start(MockRaidenService(None), "")
-
-    discovery_room_name = make_room_alias(transport.chain_id, "discovery")
-    with gevent.Timeout(1):
-        while not isinstance(transport.broadcast_rooms.get(discovery_room_name), Room):
-            gevent.sleep(0.1)
-
-    transport.stop()
-    transport.greenlet.get()
-
-
 @pytest.mark.parametrize("device_id", (DeviceIDs.PFS, DeviceIDs.MS))
 def test_matrix_broadcast(matrix_transports, services, device_id):
 
@@ -640,7 +462,7 @@ def test_matrix_broadcast(matrix_transports, services, device_id):
 
     sent_messages = list()
     for i in range(5):
-        message = Processed(message_identifier=i, signature=EMPTY_SIGNATURE)
+        message = Processed(message_identifier=MessageID(i), signature=EMPTY_SIGNATURE)
         transport._raiden_service.sign(message)
         sent_messages.append(message)
         transport.broadcast(message, device_id=device_id)
@@ -673,7 +495,7 @@ def test_monitoring_broadcast_messages(
 
     raiden_service = MockRaidenService(None)
     raiden_service.config = RaidenConfig(
-        chain_id=1234,
+        chain_id=ChainID(1234),
         environment_type=environment_type,
         services=ServiceConfig(monitoring_enabled=True),
     )
@@ -860,7 +682,6 @@ def test_pfs_broadcast_messages(
     assert isinstance(messages[1], PFSFeeUpdate)
 
 
-@pytest.mark.skip(reason="Test is still using presence / health check")
 @pytest.mark.parametrize("matrix_server_count", [3])
 @pytest.mark.parametrize("number_of_transports", [3])
 @pytest.mark.parametrize(
@@ -884,43 +705,19 @@ def test_matrix_user_roaming(matrix_transports, roaming_peer):
     transport0.start(raiden_service0, "")
     transport1.start(raiden_service1, "")
 
-    # XXX-UAM here was the health check before
-    # transport0.immediate_health_check_for(raiden_service1.address)
-    # transport1.immediate_health_check_for(raiden_service0.address)
-
     assert ping_pong_message_success(transport0, transport1)
 
     transport0.stop()
-
-    wait_for_peer_unreachable(transport1, raiden_service0.address)
-    assert not is_reachable(transport1, raiden_service0.address)
-
     transport2.start(raiden_service0, "")
-    # XXX-UAM here was the health check before
-    # transport2.immediate_health_check_for(raiden_service1.address)
 
     assert ping_pong_message_success(transport2, transport1)
 
     transport2.stop()
-
-    wait_for_peer_unreachable(transport1, raiden_service0.address)
-    assert not is_reachable(transport1, raiden_service0.address)
-
     transport0.start(raiden_service0, "")
 
-    # XXX-UAM here was the health check before
-    # transport0.immediate_health_check_for(raiden_service1.address)
-
-    with Timeout(TIMEOUT_MESSAGE_RECEIVE):
-        while not is_reachable(transport1, raiden_service0.address):
-            gevent.sleep(0.1)
-
-    assert is_reachable(transport1, raiden_service0.address)
-
     assert ping_pong_message_success(transport0, transport1)
 
 
-@pytest.mark.skip(reason="Test is still using presence / health check")
 @pytest.mark.parametrize("matrix_server_count", [3])
 @pytest.mark.parametrize("number_of_transports", [6])
 @pytest.mark.parametrize(
@@ -954,29 +751,17 @@ def test_matrix_multi_user_roaming(matrix_transports, roaming_peer):
     transport_rs0_0.start(raiden_service0, "")
     transport_rs1_0.start(raiden_service1, "")
 
-    # XXX-UAM here was the health check before
-    # transport_rs0_0.immediate_health_check_for(raiden_service1.address)
-    # transport_rs1_0.immediate_health_check_for(raiden_service0.address)
-
     assert ping_pong_message_success(transport_rs0_0, transport_rs1_0)
 
     # Node two switches to second server
     transport_rs1_0.stop()
-    wait_for_peer_unreachable(transport_rs0_0, raiden_service1.address)
-
     transport_rs1_1.start(raiden_service1, "")
-    # XXX-UAM here was the health check before
-    # transport_rs1_1.immediate_health_check_for(raiden_service0.address)
 
     assert ping_pong_message_success(transport_rs0_0, transport_rs1_1)
 
     # Node two switches to third server
     transport_rs1_1.stop()
-    wait_for_peer_unreachable(transport_rs0_0, raiden_service1.address)
-
     transport_rs1_2.start(raiden_service1, "")
-    # XXX-UAM here was the health check before
-    # transport_rs1_2.immediate_health_check_for(raiden_service0.address)
 
     assert ping_pong_message_success(transport_rs0_0, transport_rs1_2)
     # Node one switches to second server, Node two back to first
@@ -986,29 +771,17 @@ def test_matrix_multi_user_roaming(matrix_transports, roaming_peer):
     transport_rs0_1.start(raiden_service0, "")
     transport_rs1_0.start(raiden_service1, "")
 
-    # XXX-UAM here was the health check before
-    # transport_rs0_1.immediate_health_check_for(raiden_service1.address)
-    # transport_rs1_0.immediate_health_check_for(raiden_service0.address)
-
     assert ping_pong_message_success(transport_rs0_1, transport_rs1_0)
 
     # Node two joins on second server again
     transport_rs1_0.stop()
-    wait_for_peer_unreachable(transport_rs0_1, raiden_service1.address)
-
     transport_rs1_1.start(raiden_service1, "")
-    # XXX-UAM here was the health check before
-    # transport_rs1_1.immediate_health_check_for(raiden_service0.address)
 
     assert ping_pong_message_success(transport_rs0_1, transport_rs1_1)
 
     # Node two switches to third server
     transport_rs1_1.stop()
-    wait_for_peer_unreachable(transport_rs0_1, raiden_service1.address)
-
     transport_rs1_2.start(raiden_service1, "")
-    # XXX-UAM here was the health check before
-    # transport_rs1_2.immediate_health_check_for(raiden_service0.address)
 
     assert ping_pong_message_success(transport_rs0_1, transport_rs1_2)
 
@@ -1017,252 +790,23 @@ def test_matrix_multi_user_roaming(matrix_transports, roaming_peer):
     transport_rs1_2.stop()
 
     transport_rs0_2.start(raiden_service0, "")
-    # XXX-UAM here was the health check before
-    # transport_rs0_2.immediate_health_check_for(raiden_service1.address)
     transport_rs1_0.start(raiden_service1, "")
-    # XXX-UAM here was the health check before
-    # transport_rs1_0.immediate_health_check_for(raiden_service0.address)
 
     assert ping_pong_message_success(transport_rs0_2, transport_rs1_0)
 
     # Node two switches to second server
     transport_rs1_0.stop()
-    wait_for_peer_unreachable(transport_rs0_2, raiden_service1.address)
-
     transport_rs1_1.start(raiden_service1, "")
-    # XXX-UAM here was the health check before
-    # transport_rs1_1.immediate_health_check_for(raiden_service0.address)
 
     assert ping_pong_message_success(transport_rs0_2, transport_rs1_1)
 
     # Node two joins on third server
     transport_rs1_1.stop()
-    wait_for_peer_unreachable(transport_rs0_2, raiden_service1.address)
-
     transport_rs1_2.start(raiden_service1, "")
-    # XXX-UAM here was the health check before
-    # transport_rs1_2.immediate_health_check_for(raiden_service0.address)
 
     assert ping_pong_message_success(transport_rs0_2, transport_rs1_2)
 
 
-@pytest.mark.skip(reason="Test is still using presence / health check")
-@pytest.mark.parametrize("matrix_server_count", [2])
-@pytest.mark.parametrize("number_of_transports", [2])
-def test_reproduce_handle_invite_send_race_issue_3588(matrix_transports):
-    transport0, transport1 = matrix_transports
-    received_messages0 = set()
-    received_messages1 = set()
-
-    message_handler0 = MessageHandler(received_messages0)
-    message_handler1 = MessageHandler(received_messages1)
-
-    raiden_service0 = MockRaidenService(message_handler0)
-    raiden_service1 = MockRaidenService(message_handler1)
-
-    transport0.start(raiden_service0, "")
-    transport1.start(raiden_service1, "")
-    # XXX-UAM here was the health check before
-    # transport0.immediate_health_check_for(raiden_service1.address)
-    # transport1.immediate_health_check_for(raiden_service0.address)
-
-    assert ping_pong_message_success(transport0, transport1)
-
-
-@pytest.mark.parametrize("number_of_transports", [3])
-@pytest.mark.parametrize("matrix_server_count", [1])
-@pytest.mark.parametrize("matrix_sync_timeout", [5_000])  # Shorten sync timeout to prevent timeout
-def test_transport_does_not_receive_broadcast_rooms_updates(matrix_transports):
-    """Ensure that matrix server-side filters take effect on sync for broadcast room content.
-
-    Although broadcasting messages in rooms is not supported by raiden anymore,
-    we need this test to make sure that the sync filters for filtering out messages
-    in the discovery room work.
-
-    The test sets up 3 transports where:
-    Transport0 sends a message to the discovery broadcast room.
-    Transport1 has an active sync filter ID that filters out broadcast room messages.
-    Transport2 has NO active sync filter so it receives everything.
-
-    The test should wait for Transport0 to send a message and then
-    verify that Transport2 has received the message while Transport1
-    did not.
-    """
-    raiden_service0 = MockRaidenService(None)
-    raiden_service1 = MockRaidenService(None)
-    raiden_service2 = MockRaidenService(None)
-
-    transport0, transport1, transport2 = matrix_transports
-
-    received_sync_events: Dict[str, List[Dict[str, Any]]] = {"t1": [], "t2": []}
-
-    def _handle_responses(
-        name: str, responses: List[Dict[str, Any]], first_sync: bool = False
-    ):  # pylint: disable=unused-argument
-        for response in responses:
-            joined_rooms = response.get("rooms", {}).get("join", {})
-            for joined_room in joined_rooms.values():
-                timeline_events = joined_room.get("timeline").get("events", [])
-                message_events = [
-                    event for event in timeline_events if event["type"] == "m.room.message"
-                ]
-                received_sync_events[name].extend(message_events)
-
-    # Replace the transport's handle_response method
-    # Should be able to detect if sync delivered a message
-    transport1._client._handle_responses = partial(_handle_responses, "t1")
-    transport2._client._handle_responses = partial(_handle_responses, "t2")
-
-    transport0.start(raiden_service0, None)
-    transport1.start(raiden_service1, None)
-    transport2.start(raiden_service2, None)
-
-    discovery_room_alias = make_room_alias(transport0.chain_id, DISCOVERY_DEFAULT_ROOM)
-    discovery_broadcast_room_t0 = transport0.broadcast_rooms[discovery_room_alias]
-
-    # Get the sync helper to control flow of asynchronous syncs
-    sync_progress1 = transport1._client.sync_progress
-    sync_progress2 = transport2._client.sync_progress
-
-    # Reset transport2 sync filter identifier so that
-    # we can receive broadcast messages
-    assert transport2._client._sync_filter_id is not None
-    transport2._client._sync_filter_id = None
-
-    # get the last sync tokens to control the processed state later
-    last_synced_token1 = sync_progress1.last_synced
-    # for T2 we need to make sure that the current sync used the filter reset -> wait()
-    last_synced_token2 = sync_progress2.synced_event.wait()[0]
-    # Send another message to the broadcast room, if transport1 listens on the room it will
-    # throw an exception
-    message = Processed(message_identifier=1, signature=EMPTY_SIGNATURE)
-    message_text = MessageSerializer.serialize(message)
-    discovery_broadcast_room_t0.send_text(message_text)
-
-    # wait for the current tokens to be processed + 1 additional sync
-    # this must be done because the message should be in the sync after the stored token
-    sync_progress1.wait_for_processed(last_synced_token1, 1)
-    sync_progress2.wait_for_processed(last_synced_token2, 1)
-
-    # Transport2 should have received the message
-    assert received_sync_events["t2"]
-    event_body = received_sync_events["t2"][0]["content"]["body"]
-    assert message_text == event_body
-
-    # Transport1 used the filter so nothing was received
-    assert not received_sync_events["t1"]
-
-
-@raise_on_failure
-@pytest.mark.skip(reason="Test is still using presence / health check")
-@pytest.mark.parametrize("matrix_server_count", [3])
-@pytest.mark.parametrize("number_of_nodes", [3])
-def test_transport_presence_updates(
-    raiden_network: List[RaidenService], restart_node, retry_timeout
-):
-    """
-    Create transports and test that matrix delivers presence updates
-    in the presence of filters which ignore all event updates
-    from matrix for broadcast rooms except for the presence events.
-    """
-
-    app0, app1, app2 = raiden_network
-
-    # XXX-UAM here was the health check before
-    # app0.transport.immediate_health_check_for(app1.address)
-    # app0.transport.immediate_health_check_for(app2.address)
-    #
-    # app1.transport.immediate_health_check_for(app0.address)
-    # app1.transport.immediate_health_check_for(app2.address)
-    #
-    # app2.transport.immediate_health_check_for(app0.address)
-    # app2.transport.immediate_health_check_for(app1.address)
-
-    wait_for_network_state(app0, app1.address, NetworkState.REACHABLE, retry_timeout)
-    wait_for_network_state(app0, app2.address, NetworkState.REACHABLE, retry_timeout)
-
-    wait_for_network_state(app1, app0.address, NetworkState.REACHABLE, retry_timeout)
-    wait_for_network_state(app1, app2.address, NetworkState.REACHABLE, retry_timeout)
-
-    wait_for_network_state(app2, app0.address, NetworkState.REACHABLE, retry_timeout)
-    wait_for_network_state(app2, app1.address, NetworkState.REACHABLE, retry_timeout)
-
-    # Stop app0
-    app0.stop()
-    wait_for_network_state(app1, app0.address, NetworkState.UNREACHABLE, retry_timeout)
-    wait_for_network_state(app2, app0.address, NetworkState.UNREACHABLE, retry_timeout)
-
-    # Restart app0
-    restart_node(app0)
-    # XXX-UAM here was the health check before
-    # app0.transport.immediate_health_check_for(app1.address)
-    # app0.transport.immediate_health_check_for(app2.address)
-    wait_for_network_state(app1, app0.address, NetworkState.REACHABLE, retry_timeout)
-    wait_for_network_state(app2, app0.address, NetworkState.REACHABLE, retry_timeout)
-
-    # Stop app1
-    app1.stop()
-    wait_for_network_state(app0, app1.address, NetworkState.UNREACHABLE, retry_timeout)
-    wait_for_network_state(app2, app1.address, NetworkState.UNREACHABLE, retry_timeout)
-
-    # Restart app1
-    restart_node(app1)
-    # XXX-UAM here was the health check before
-    # app1.transport.immediate_health_check_for(app0.address)
-    # app1.transport.immediate_health_check_for(app2.address)
-    wait_for_network_state(app0, app1.address, NetworkState.REACHABLE, retry_timeout)
-    wait_for_network_state(app2, app1.address, NetworkState.REACHABLE, retry_timeout)
-
-    # Stop app2
-    app2.stop()
-    wait_for_network_state(app0, app2.address, NetworkState.UNREACHABLE, retry_timeout)
-    wait_for_network_state(app1, app2.address, NetworkState.UNREACHABLE, retry_timeout)
-
-    # Restart app0
-    app2.start()
-    # XXX-UAM here was the health check before
-    # app2.transport.immediate_health_check_for(app0.address)
-    # app2.transport.immediate_health_check_for(app1.address)
-    wait_for_network_state(app0, app2.address, NetworkState.REACHABLE, retry_timeout)
-    wait_for_network_state(app1, app2.address, NetworkState.REACHABLE, retry_timeout)
-
-
-@raise_on_failure
-@pytest.mark.skip(reason="Test is still using presence / health check")
-@pytest.mark.parametrize("matrix_server_count", [1])
-@pytest.mark.parametrize("number_of_nodes", [2])
-@pytest.mark.parametrize("adhoc_capability", [True])
-def test_transport_capabilities(raiden_network: List[RaidenService], capabilities, retry_timeout):
-    """
-    Test that raiden matrix users have the `avatar_url` set in a format understood
-    by the capabilities parser.
-    """
-    app0, app1 = raiden_network
-
-    # XXX-UAM here was the health check before
-    # app0.transport.immediate_health_check_for(app1.address)
-    # app1.transport.immediate_health_check_for(app0.address)
-
-    wait_for_network_state(app0, app1.address, NetworkState.REACHABLE, retry_timeout)
-    wait_for_network_state(app1, app0.address, NetworkState.REACHABLE, retry_timeout)
-
-    # expected_capabilities = capconfig_to_dict(capabilities)
-
-    # XXX-UAM: get_user_ids_for_address was called here
-    # app1_user_ids = app0.transport.get_user_ids_for_address(app1.address)
-    # assert len(app1_user_ids) == 1, "app1 should have exactly one user_id"
-    # app1_user = app0.transport._client.get_user(app1_user_ids.pop())
-    # app1_avatar_url = app1_user.get_avatar_url()
-    # assert len(app1_avatar_url), "avatar_url not set for app1"
-    # app1_capabilities = deserialize_capabilities(app1_avatar_url)
-    # assert "adhoc_capability" in app1_capabilities, "capabilities could not be parsed correctly"
-
-    # XXX-UAM uam was accessed here
-    # collected_capabilities = app0.transport._address_mgr.get_address_capabilities(app1.address)
-    # msg = "capabilities were not collected in transport client"
-    # assert collected_capabilities == PeerCapabilities(expected_capabilities), msg
-
-
 def test_populate_services_addresses(
     service_registry_address, private_keys, web3, contract_manager
 ):
diff --git a/raiden/tests/integration/network/transport/test_matrix_transport_assumptions.py b/raiden/tests/integration/network/transport/test_matrix_transport_assumptions.py
index cd1a73b379..f4c861627f 100644
--- a/raiden/tests/integration/network/transport/test_matrix_transport_assumptions.py
+++ b/raiden/tests/integration/network/transport/test_matrix_transport_assumptions.py
@@ -1,8 +1,6 @@
 import time
 from collections import defaultdict
 from contextlib import contextmanager
-from functools import partial
-from urllib.parse import urlsplit
 
 import gevent
 import pytest
@@ -14,27 +12,13 @@
 from raiden.network.transport.matrix.utils import (
     UserPresence,
     address_from_userid,
-    join_broadcast_room,
     login,
     make_client,
     make_room_alias,
 )
-from raiden.settings import (
-    DEFAULT_TRANSPORT_MATRIX_SYNC_LATENCY,
-    DEFAULT_TRANSPORT_MATRIX_SYNC_TIMEOUT,
-)
 from raiden.tests.utils import factories
 from raiden.tests.utils.detect_failure import raise_on_failure
-from raiden.tests.utils.factories import UNIT_CHAIN_ID
-from raiden.tests.utils.mocks import MockRaidenService
-from raiden.tests.utils.transport import (
-    get_admin_credentials,
-    ignore_member_join,
-    ignore_messages,
-    new_client,
-)
-from raiden.utils.formatting import to_hex_address
-from raiden.utils.http import HTTPExecutor
+from raiden.tests.utils.transport import ignore_messages, new_client
 from raiden.utils.signer import Signer
 from raiden.utils.typing import Address, Any, Dict, Generator, Tuple
 
@@ -52,7 +36,7 @@ def must_run_for_at_least(minimum_elapsed_time: float, msg: str) -> Generator:
 
 
 def create_logged_in_client(server: str) -> Tuple[GMatrixClient, Signer]:
-    client = make_client(ignore_messages, ignore_member_join, [server])
+    client = make_client(ignore_messages, [server])
     signer = factories.make_signer()
 
     login(client, signer, DeviceIDs.RAIDEN)
@@ -253,44 +237,6 @@ def long_polling():
     assert long_running.successful(), "Thread calling sync failed"
 
 
-@pytest.mark.parametrize("matrix_server_count", [2])
-def test_assumption_search_user_directory_returns_federated_users(chain_id, local_matrix_servers):
-    """The search_user_directory should return federated users.
-
-    This assumption test was added because of issue #5285. The
-    path-finding-service was not functioning properly because the call to
-    `search_user_directory` did not return federated users, only local users.
-    Becaused of that the PFS assumed the users were offline and didn't find any
-    valid routes for the payments.
-    """
-    original_server_url = urlsplit(local_matrix_servers[0]).netloc
-
-    room_alias = make_room_alias(chain_id, "broadcast_test")
-    room_name_full = f"#{room_alias}:{original_server_url}"
-
-    user_room_creator, _ = create_logged_in_client(local_matrix_servers[0])
-    user_room_creator.create_room(room_alias, is_public=True)
-
-    user_federated, _ = create_logged_in_client(local_matrix_servers[1])
-    join_broadcast_room(user_federated, room_name_full)
-
-    addresses = list()
-    for _ in range(1000):
-        user, signer = create_logged_in_client(local_matrix_servers[0])
-        join_broadcast_room(user, room_name_full)
-
-        # Make sure to close the session instance, otherwise there will be too
-        # many file descriptors opened by the underlying urllib3 connection
-        # pool.
-        user.api.session.close()
-        del user
-
-        addresses.append(signer.address)
-
-    for address in addresses:
-        assert user_federated.search_user_directory(to_hex_address(address))
-
-
 @pytest.mark.parametrize("matrix_server_count", [3])
 def test_assumption_cannot_override_room_alias(local_matrix_servers):
     """Issue: https://github.com/raiden-network/raiden/issues/5366
@@ -312,7 +258,7 @@ def test_assumption_cannot_override_room_alias(local_matrix_servers):
     public_room = next(iter(server1_client.rooms.values()))
 
     for local_server in local_matrix_servers[1:]:
-        client = new_client(ignore_messages, ignore_member_join, local_server)
+        client = new_client(ignore_messages, local_server)
         assert public_room.room_id not in client.rooms
         client.join_room(public_room.canonical_alias)
         assert public_room.room_id in client.rooms
@@ -333,87 +279,6 @@ def test_assumption_cannot_override_room_alias(local_matrix_servers):
             client2.create_room(room_alias_prefix, is_public=True)
 
 
-@pytest.mark.parametrize("matrix_server_count", [3])
-def test_assumption_federation_works_after_original_server_goes_down(
-    chain_id, local_matrix_servers_with_executor
-):
-    """Check that a federated broadcast room keeps working after the original server goes down.
-
-    This creates a federation of three matrix servers and a client for each.
-    It then checks that all nodes receive messages from the broadcast room.
-    Then the first matrix server is shut down and a second message send to
-    the broadcast room, which should arrive at both remaining clients.
-    """
-    original_server_url = urlsplit(local_matrix_servers_with_executor[0][0]).netloc
-
-    room_alias = make_room_alias(chain_id, "broadcast_test")
-    room_name_full = f"#{room_alias}:{original_server_url}"
-
-    user_room_creator, _ = create_logged_in_client(local_matrix_servers_with_executor[0][0])
-    original_room: Room = user_room_creator.create_room(room_alias, is_public=True)
-    user_room_creator.start_listener_thread(
-        timeout_ms=DEFAULT_TRANSPORT_MATRIX_SYNC_TIMEOUT,
-        latency_ms=DEFAULT_TRANSPORT_MATRIX_SYNC_LATENCY,
-    )
-
-    user_federated_1, _ = create_logged_in_client(local_matrix_servers_with_executor[1][0])
-    room_server1 = join_broadcast_room(user_federated_1, room_name_full)
-    user_federated_1.rooms[room_server1.room_id] = room_server1
-    user_federated_1.start_listener_thread(
-        timeout_ms=DEFAULT_TRANSPORT_MATRIX_SYNC_TIMEOUT,
-        latency_ms=DEFAULT_TRANSPORT_MATRIX_SYNC_LATENCY,
-    )
-
-    user_federated_2, _ = create_logged_in_client(local_matrix_servers_with_executor[2][0])
-    room_server2 = join_broadcast_room(user_federated_2, room_name_full)
-    user_federated_2.rooms[room_server2.room_id] = room_server2
-    user_federated_2.start_listener_thread(
-        timeout_ms=DEFAULT_TRANSPORT_MATRIX_SYNC_TIMEOUT,
-        latency_ms=DEFAULT_TRANSPORT_MATRIX_SYNC_LATENCY,
-    )
-
-    received = {}
-
-    def handle_message(node_id: int, _room: Room, event: Dict[str, Any]):
-        nonlocal received
-        received[node_id] = event["content"]["body"]
-
-    original_room.add_listener(partial(handle_message, 0), "m.room.message")
-    room_server1.add_listener(partial(handle_message, 1), "m.room.message")
-    room_server2.add_listener(partial(handle_message, 2), "m.room.message")
-
-    # Full federation, send a message to check it works
-    original_room.send_text("Message1")
-
-    while not len(received) == 3:
-        gevent.sleep(0.1)
-
-    assert sorted(received.keys()) == [0, 1, 2]
-    assert all("Message1" == m for m in received.values())
-
-    # Shut down the room_creator before we stop the server
-    user_room_creator.stop_listener_thread()
-    # Shutdown server 0, the original creator of the room
-    server: HTTPExecutor = local_matrix_servers_with_executor[0][1]
-    server.stop()
-
-    # Send message from client 1, check that client 2 receives it
-    received = {}
-    room_server1.send_text("Message2")
-
-    while not len(received) == 2:
-        gevent.sleep(0.1)
-
-    assert sorted(received.keys()) == [1, 2]
-    assert all("Message2" == m for m in received.values())
-
-    # Shut down longrunning threads
-    user_federated_1.stop_listener_thread()
-    user_federated_2.stop_listener_thread()
-
-    # TODO: restart matrix server 1, check that message 2 arrives
-
-
 @pytest.mark.parametrize("matrix_server_count", [1])
 def test_assumption_matrix_returns_same_id_for_same_filter_payload(chain_id, local_matrix_servers):
     """
@@ -437,72 +302,6 @@ def test_assumption_matrix_returns_same_id_for_same_filter_payload(chain_id, loc
     assert first_sync_filter_id == second_sync_filter_id
 
 
-@pytest.mark.parametrize("number_of_transports", [3])
-@pytest.mark.parametrize("matrix_server_count", [1])
-def test_admin_is_allowed_to_kick(matrix_transports, local_matrix_servers):
-    server_name = local_matrix_servers[0].netloc
-    admin_credentials = get_admin_credentials(server_name)
-    broadcast_room_name = make_room_alias(UNIT_CHAIN_ID, "discovery")
-    broadcast_room_alias = f"#{broadcast_room_name}:{server_name}"
-
-    transport0, transport1, transport2 = matrix_transports
-
-    raiden_service0 = MockRaidenService()
-    raiden_service1 = MockRaidenService()
-    # start transports to join broadcast rooms as normal users
-    transport0.start(raiden_service0, None)
-    transport1.start(raiden_service1, None)
-    # admin login using raiden.tests.utils.transport.AdminAuthProvider
-    admin_client = GMatrixClient(ignore_messages, ignore_member_join, local_matrix_servers[0])
-    admin_client.login(admin_credentials["username"], admin_credentials["password"], sync=False)
-    room_id = admin_client.join_room(broadcast_room_alias).room_id
-
-    # get members of room and filter not kickable users (power level 100)
-    def _get_joined_room_members():
-        membership_events = admin_client.api.get_room_members(room_id)["chunk"]
-        member_ids = [
-            event["state_key"]
-            for event in membership_events
-            if event["content"]["membership"] == "join"
-        ]
-        return set(member_ids)
-
-    members = _get_joined_room_members()
-    power_levels_event = admin_client.api.get_power_levels(room_id)
-    admin_user_ids = [key for key, value in power_levels_event["users"].items() if value >= 50]
-    non_admin_user_ids = [member for member in members if member not in admin_user_ids]
-    # transport0 and transport1 should still be in non_admin_user_ids
-    assert len(non_admin_user_ids) > 1
-    kick_user_id = non_admin_user_ids[0]
-
-    # kick one user
-    admin_client.api.kick_user(room_id, kick_user_id)
-
-    # Assert missing member
-    members_after_kick = _get_joined_room_members()
-    assert len(members_after_kick) == len(members) - 1
-    members_after_kick.add(kick_user_id)
-    assert members_after_kick == members
-
-    # check assumption that new user does not receive presence
-    raiden_service2 = MockRaidenService()
-
-    def local_presence_listener(event, event_id):  # pylint: disable=unused-argument
-        assert event["sender"] != kick_user_id
-
-    transport2._client.add_presence_listener(local_presence_listener)
-    transport2.start(raiden_service2, None)
-
-    transport2.stop()
-
-    # rejoin and assert that normal user cannot kick
-    kicked_transport = transport0 if transport0._user_id == kick_user_id else transport1
-    kicked_transport._client.join_room(broadcast_room_alias)
-
-    with pytest.raises(MatrixRequestError):
-        kicked_transport._client.api.kick_user(room_id, non_admin_user_ids[1])
-
-
 @raise_on_failure
 @pytest.mark.parametrize("number_of_nodes", [2])
 def test_assumption_broadcast_queue_delays_shutdown(raiden_chain):
diff --git a/raiden/tests/integration/network/transport/test_web_rtc.py b/raiden/tests/integration/network/transport/test_web_rtc.py
index 842b59e023..07a6ae1595 100644
--- a/raiden/tests/integration/network/transport/test_web_rtc.py
+++ b/raiden/tests/integration/network/transport/test_web_rtc.py
@@ -27,7 +27,7 @@ def on_messages(self, _, messages):
         self.bag.update(messages)
 
 
-@pytest.mark.skip(reason="Test is still using presence / health check")
+@pytest.mark.skip(reason="web RTC is disabled")
 @pytest.mark.parametrize("matrix_server_count", [1])
 @pytest.mark.parametrize("number_of_transports", [2])
 @pytest.mark.parametrize("capabilities", [CapabilitiesConfig(web_rtc=True)])
@@ -49,9 +49,6 @@ def mock_handle_web_rtc_messages(message_data, partner_address):
     transport0.start(raiden_service0, None)
     transport1.start(raiden_service1, None)
 
-    transport0.immediate_health_check_for(transport1._raiden_service.address)
-    transport1.immediate_health_check_for(transport0._raiden_service.address)
-
     with Timeout(TIMEOUT_WEB_RTC_CONNECTION):
         # wait until web rtc connection is ready
         while not transport0._web_rtc_manager.has_ready_channel(raiden_service1.address):
diff --git a/raiden/tests/integration/rpc/assumptions/test_geth_rpc_assumptions.py b/raiden/tests/integration/rpc/assumptions/test_geth_rpc_assumptions.py
index bb26b77960..23216524cd 100644
--- a/raiden/tests/integration/rpc/assumptions/test_geth_rpc_assumptions.py
+++ b/raiden/tests/integration/rpc/assumptions/test_geth_rpc_assumptions.py
@@ -21,7 +21,7 @@
 def test_geth_request_pruned_data_raises_an_exception(
     deploy_client: JSONRPCClient, web3: Web3
 ) -> None:
-    """ Interacting with an old block identifier with a pruning client throws. """
+    """Interacting with an old block identifier with a pruning client throws."""
     contract_proxy, _ = deploy_rpc_test_contract(deploy_client, "RpcWithStorageTest")
     iterations = 1
 
diff --git a/raiden/tests/integration/rpc/assumptions/test_parity_rpc_assumptions.py b/raiden/tests/integration/rpc/assumptions/test_parity_rpc_assumptions.py
index 500bff3b09..3a62290961 100644
--- a/raiden/tests/integration/rpc/assumptions/test_parity_rpc_assumptions.py
+++ b/raiden/tests/integration/rpc/assumptions/test_parity_rpc_assumptions.py
@@ -20,7 +20,7 @@
 
 @pytest.mark.parametrize("blockchain_extra_config", [STATE_PRUNING])
 def test_parity_request_pruned_data_raises_an_exception(deploy_client: JSONRPCClient) -> None:
-    """ Interacting with an old block identifier with a pruning client throws. """
+    """Interacting with an old block identifier with a pruning client throws."""
     contract_proxy, _ = deploy_rpc_test_contract(deploy_client, "RpcWithStorageTest")
     iterations = 1000
 
diff --git a/raiden/tests/integration/rpc/assumptions/test_rpc_call_assumptions.py b/raiden/tests/integration/rpc/assumptions/test_rpc_call_assumptions.py
index 6fcac37ec5..c2137db931 100644
--- a/raiden/tests/integration/rpc/assumptions/test_rpc_call_assumptions.py
+++ b/raiden/tests/integration/rpc/assumptions/test_rpc_call_assumptions.py
@@ -23,7 +23,7 @@ def test_call_invalid_selector(deploy_client: JSONRPCClient) -> None:
 
 
 def test_call_inexisting_address(deploy_client: JSONRPCClient) -> None:
-    """ A JSON RPC call to an inexisting address returns the empty string. """
+    """A JSON RPC call to an inexisting address returns the empty string."""
 
     inexisting_address = b"\x01\x02\x03\x04\x05" * 4
 
@@ -69,7 +69,7 @@ def test_call_which_returns_a_string_before_smart_contract_deployed(
 
 
 def test_call_works_with_blockhash(deploy_client: JSONRPCClient) -> None:
-    """ A JSON RPC call works with a block number or blockhash. """
+    """A JSON RPC call works with a block number or blockhash."""
     contract_proxy, receipt = deploy_rpc_test_contract(deploy_client, "RpcTest")
 
     deploy_blockhash = receipt["blockHash"]
@@ -80,7 +80,7 @@ def test_call_works_with_blockhash(deploy_client: JSONRPCClient) -> None:
 
 
 def test_call_throws(deploy_client: JSONRPCClient) -> None:
-    """ A JSON RPC call to a function that throws/gets reverted returns the empty string. """
+    """A JSON RPC call to a function that throws/gets reverted returns the empty string."""
     contract_proxy, _ = deploy_rpc_test_contract(deploy_client, "RpcTest")
 
     address = contract_proxy.address
diff --git a/raiden/tests/integration/rpc/assumptions/test_rpc_filters_assumptions.py b/raiden/tests/integration/rpc/assumptions/test_rpc_filters_assumptions.py
index 3066d7ca53..69e0264bff 100644
--- a/raiden/tests/integration/rpc/assumptions/test_rpc_filters_assumptions.py
+++ b/raiden/tests/integration/rpc/assumptions/test_rpc_filters_assumptions.py
@@ -5,7 +5,7 @@
 
 
 def test_filter_start_block_inclusive(deploy_client: JSONRPCClient) -> None:
-    """ A filter includes events from the block given in from_block """
+    """A filter includes events from the block given in from_block"""
     contract_proxy, _ = deploy_rpc_test_contract(deploy_client, "RpcTest")
 
     # call the create event function twice and wait for confirmation each time
diff --git a/raiden/tests/integration/rpc/assumptions/test_rpc_gas_estimation_assumptions.py b/raiden/tests/integration/rpc/assumptions/test_rpc_gas_estimation_assumptions.py
index 966cc66626..2ce9da9351 100644
--- a/raiden/tests/integration/rpc/assumptions/test_rpc_gas_estimation_assumptions.py
+++ b/raiden/tests/integration/rpc/assumptions/test_rpc_gas_estimation_assumptions.py
@@ -8,7 +8,7 @@
 
 
 def test_estimate_gas_fail(deploy_client: JSONRPCClient) -> None:
-    """ A JSON RPC estimate gas call for a throwing transaction returns None"""
+    """A JSON RPC estimate gas call for a throwing transaction returns None"""
     contract_proxy, _ = deploy_rpc_test_contract(deploy_client, "RpcTest")
 
     address = contract_proxy.address
diff --git a/raiden/tests/integration/rpc/assumptions/test_rpc_gas_price_assumptions.py b/raiden/tests/integration/rpc/assumptions/test_rpc_gas_price_assumptions.py
index b132a6b7d4..3a039f4d82 100644
--- a/raiden/tests/integration/rpc/assumptions/test_rpc_gas_price_assumptions.py
+++ b/raiden/tests/integration/rpc/assumptions/test_rpc_gas_price_assumptions.py
@@ -70,7 +70,7 @@ def test_resending_pending_transaction_raises(deploy_client: JSONRPCClient) -> N
 
 
 def test_resending_mined_transaction_raises(deploy_client: JSONRPCClient) -> None:
-    """ If a mined transaction is re-sent the exception `EthereumNonceTooLow` is raised. """
+    """If a mined transaction is re-sent the exception `EthereumNonceTooLow` is raised."""
     # Use a _fixed_ gas price strategy so that both transactions are identical.
     deploy_client.web3.eth.setGasPriceStrategy(make_fixed_gas_price_strategy(GasPrice(2000000000)))
     contract_proxy, _ = deploy_rpc_test_contract(deploy_client, "RpcTest")
@@ -249,7 +249,7 @@ def test_remote_transaction_with_zero_gasprice_is_not_mined(
 
 
 def test_resending_pending_transaction_with_lower_gas_raises(deploy_client: JSONRPCClient) -> None:
-    """ If the same transaction is sent twice a JSON RPC error is raised. """
+    """If the same transaction is sent twice a JSON RPC error is raised."""
     # Use a _decreasing_ gas price strategy so that the second transactions is
     # lower than the first.
     deploy_client.web3.eth.setGasPriceStrategy(
diff --git a/raiden/tests/integration/rpc/assumptions/test_rpc_transaction_assumptions.py b/raiden/tests/integration/rpc/assumptions/test_rpc_transaction_assumptions.py
index 8a09642630..cda16e45ed 100644
--- a/raiden/tests/integration/rpc/assumptions/test_rpc_transaction_assumptions.py
+++ b/raiden/tests/integration/rpc/assumptions/test_rpc_transaction_assumptions.py
@@ -20,7 +20,7 @@
 
 
 def test_transact_opcode(deploy_client: JSONRPCClient) -> None:
-    """ The receipt status field of a transaction that did not throw is 0x1 """
+    """The receipt status field of a transaction that did not throw is 0x1"""
     contract_proxy, _ = deploy_rpc_test_contract(deploy_client, "RpcTest")
 
     address = contract_proxy.address
@@ -36,7 +36,7 @@ def test_transact_opcode(deploy_client: JSONRPCClient) -> None:
 
 
 def test_transact_throws_opcode(deploy_client: JSONRPCClient) -> None:
-    """ The receipt status field of a transaction that hit an assert or require is 0x0 """
+    """The receipt status field of a transaction that hit an assert or require is 0x0"""
     contract_proxy, _ = deploy_rpc_test_contract(deploy_client, "RpcTest")
 
     address = to_canonical_address(contract_proxy.address)
@@ -79,7 +79,7 @@ def test_transact_throws_opcode(deploy_client: JSONRPCClient) -> None:
 
 
 def test_transact_opcode_oog(deploy_client: JSONRPCClient) -> None:
-    """ The receipt status field of a transaction that did NOT throw is 0x0. """
+    """The receipt status field of a transaction that did NOT throw is 0x0."""
     contract_proxy, _ = deploy_rpc_test_contract(deploy_client, "RpcTest")
 
     address = contract_proxy.address
diff --git a/raiden/tests/integration/test_balance_proof_check.py b/raiden/tests/integration/test_balance_proof_check.py
index c474b9e124..d0fe97b352 100644
--- a/raiden/tests/integration/test_balance_proof_check.py
+++ b/raiden/tests/integration/test_balance_proof_check.py
@@ -67,6 +67,7 @@ def test_node_can_settle_if_close_didnt_use_any_balance_proof(
         amount=PaymentAmount(1),
         identifier=PaymentID(1),
         timeout=network_wait * number_of_nodes,
+        routes=[[app0, app1]],
     )
     # stop app1 - the test uses token_network_contract now
     app1.stop()
@@ -154,6 +155,7 @@ def test_node_can_settle_if_partner_does_not_call_update_transfer(
         amount=PaymentAmount(1),
         identifier=PaymentID(1),
         timeout=network_wait * number_of_nodes,
+        routes=[[app0, app1]],
     )
     # stop app1 - the test uses token_network_contract now
     app1.stop()
diff --git a/raiden/tests/integration/test_integration_pfs.py b/raiden/tests/integration/test_integration_pfs.py
index bd12a1acb5..69f165aab5 100644
--- a/raiden/tests/integration/test_integration_pfs.py
+++ b/raiden/tests/integration/test_integration_pfs.py
@@ -67,7 +67,7 @@ def wait_all_apps(raiden_network: List[RaidenService]) -> None:
 @pytest.mark.parametrize("channels_per_node", [0])
 @pytest.mark.parametrize("routing_mode", [RoutingMode.PFS])
 def test_pfs_send_capacity_updates_on_deposit_and_withdraw(
-    raiden_network: List[RaidenService], token_addresses: List[TokenAddress]
+    raiden_network: List[RaidenService], token_addresses: List[TokenAddress], pfs_mock
 ) -> None:
     """
     We need to test if PFSCapacityUpdates and PFSFeeUpdates are being
@@ -77,6 +77,8 @@ def test_pfs_send_capacity_updates_on_deposit_and_withdraw(
     withdraw it is checked that the correct messages are sent.
     """
     app0, app1, app2 = raiden_network
+    pfs_mock.add_apps(raiden_network)
+
     api0 = RaidenAPI(app0)
     api0.channel_open(
         token_address=token_addresses[0],
diff --git a/raiden/tests/integration/test_raidenservice.py b/raiden/tests/integration/test_raidenservice.py
index 9a202e9b40..7995acd39e 100644
--- a/raiden/tests/integration/test_raidenservice.py
+++ b/raiden/tests/integration/test_raidenservice.py
@@ -103,6 +103,7 @@ def test_broadcast_messages_must_be_sent_before_protocol_messages_on_restarts(
         amount=amount,
         identifier=payment_id,
         timeout=network_wait * number_of_nodes,
+        routes=[[app1, app0]],
     )
 
     app0.stop()
diff --git a/raiden/tests/integration/test_recovery.py b/raiden/tests/integration/test_recovery.py
index b81dbea604..2beae107a4 100644
--- a/raiden/tests/integration/test_recovery.py
+++ b/raiden/tests/integration/test_recovery.py
@@ -21,7 +21,6 @@
 )
 from raiden.transfer import views
 from raiden.transfer.events import ContractSendChannelWithdraw
-from raiden.transfer.state import NetworkState
 from raiden.transfer.state_change import (
     ContractReceiveChannelClosed,
     ContractReceiveChannelSettled,
@@ -68,8 +67,6 @@ def test_recovery_happy_case(
 
     app0.stop()
 
-    waiting.wait_for_network_state(app1, app0.address, NetworkState.UNREACHABLE, network_wait)
-
     restart_node(app0)
 
     assert_synced_channel_state(
@@ -79,10 +76,6 @@ def test_recovery_happy_case(
         token_network_address, app1, deposit - spent_amount, [], app2, deposit + spent_amount, []
     )
 
-    # wait for the nodes' healthcheck to update the network statuses
-    waiting.wait_for_healthy(app0, app1.address, network_wait)
-    waiting.wait_for_healthy(app1, app0.address, network_wait)
-
     transfer_and_assert_path(
         path=raiden_network[::-1],
         token_address=token_address,
@@ -202,12 +195,11 @@ def test_recovery_unhappy_case(
 
 
 @raise_on_failure
-@pytest.mark.skip(reason="Test is still using presence / health check")
 @pytest.mark.parametrize("deposit", [10])
 @pytest.mark.parametrize("channels_per_node", [CHAIN])
 @pytest.mark.parametrize("number_of_nodes", [2])
 def test_recovery_blockchain_events(
-    raiden_network: List[RaidenService], restart_node, token_addresses, network_wait
+    raiden_network: List[RaidenService], restart_node, token_addresses
 ):
     """Close one of the two raiden apps that have a channel between them,
     have the counterparty close the channel and then make sure the restarted
@@ -256,9 +248,6 @@ def test_recovery_blockchain_events(
     wal = app0_restart.wal
     assert wal
 
-    # wait for the nodes' healthcheck to update the network statuses
-    waiting.wait_for_healthy(app0_restart, app1.address, network_wait)
-    waiting.wait_for_healthy(app1, app0_restart.address, network_wait)
     restarted_state_changes = wal.storage.get_statechanges_by_range(RANGE_ALL_STATE_CHANGES)
     assert search_for_item(restarted_state_changes, ContractReceiveChannelClosed, {})
 
@@ -273,6 +262,7 @@ def test_node_clears_pending_withdraw_transaction_after_channel_is_closed(
     network_wait,
     number_of_nodes,
     retry_timeout,
+    pfs_mock,
 ):
     """A test case related to https://github.com/raiden-network/raiden/issues/4639
     where a node sends a withdraw transaction, is stopped before the transaction is completed.
@@ -283,6 +273,7 @@ def test_node_clears_pending_withdraw_transaction_after_channel_is_closed(
     the on-chain transaction fails.
     """
     app0, app1 = raiden_network
+    pfs_mock.add_apps(raiden_network)
     token_address = token_addresses[0]
 
     # Prevent the withdraw transaction from being sent on-chain. This
@@ -298,8 +289,11 @@ def test_node_clears_pending_withdraw_transaction_after_channel_is_closed(
     )
     assert channel_state, "Channel does not exist"
 
+    app1_metadata = pfs_mock.query_address_metadata(app0.config.pfs_config, app1.address)
     app0.withdraw(
-        canonical_identifier=channel_state.canonical_identifier, total_withdraw=WithdrawAmount(1)
+        canonical_identifier=channel_state.canonical_identifier,
+        total_withdraw=WithdrawAmount(1),
+        recipient_metadata=app1_metadata,
     )
 
     timeout = network_wait * number_of_nodes
diff --git a/raiden/tests/integration/test_regression.py b/raiden/tests/integration/test_regression.py
index 7901a6d2f6..88d8b0efa9 100644
--- a/raiden/tests/integration/test_regression.py
+++ b/raiden/tests/integration/test_regression.py
@@ -8,10 +8,7 @@
 from raiden.raiden_service import RaidenService
 from raiden.tests.integration.fixtures.raiden_network import CHAIN, wait_for_channels
 from raiden.tests.utils.detect_failure import raise_on_failure
-from raiden.tests.utils.events import (
-    raiden_events_search_for_item,
-    raiden_state_changes_search_for_item,
-)
+from raiden.tests.utils.events import raiden_events_search_for_item
 from raiden.tests.utils.factories import (
     UNIT_CHAIN_ID,
     make_message_identifier,
@@ -25,8 +22,7 @@
     watch_for_unlock_failures,
 )
 from raiden.transfer import views
-from raiden.transfer.mediated_transfer.events import EventRouteFailed, SendSecretReveal
-from raiden.transfer.mediated_transfer.state_change import ReceiveTransferCancelRoute
+from raiden.transfer.mediated_transfer.events import SendSecretReveal
 from raiden.utils.typing import (
     BlockExpiration,
     InitiatorAddress,
@@ -243,52 +239,3 @@ def test_regression_register_secret_once(secret_registry_address, proxy_manager)
     previous_nonce = proxy_manager.client._available_nonce
     secret_registry.register_secret_batch(secrets=[secret])
     assert previous_nonce == proxy_manager.client._available_nonce
-
-
-@raise_on_failure
-@pytest.mark.parametrize("number_of_nodes", [5])
-@pytest.mark.parametrize("channels_per_node", [0])
-def test_regression_payment_complete_after_refund_to_the_initiator(
-    raiden_network: List[RaidenService], token_addresses, settle_timeout, deposit
-):
-    """Regression test for issue #3915"""
-    app0, app1, app2, app3, app4 = raiden_network
-    token = token_addresses[0]
-    registry_address = app0.default_registry.address
-
-    # Topology:
-    #
-    #  0 -> 1 -> 2
-    #  |         ^
-    #  v         |
-    #  3 ------> 4
-
-    app_channels = [(app0, app1), (app1, app2), (app0, app3), (app3, app4), (app4, app2)]
-    open_and_wait_for_channels(app_channels, registry_address, token, deposit, settle_timeout)
-
-    # Use all deposit from app1->app2 to force a refund
-    transfer(
-        initiator_app=app1,
-        target_app=app2,
-        token_address=token,
-        amount=PaymentAmount(deposit),
-        identifier=PaymentID(1),
-        routes=[[app1, app2]],
-    )
-
-    # Send a transfer that will result in a refund app1->app0
-    transfer(
-        initiator_app=app0,
-        target_app=app2,
-        token_address=token,
-        amount=PaymentAmount(50),
-        identifier=PaymentID(2),
-        timeout=20,
-        expect_unlock_failures=True,
-        routes=[[app0, app1, app2], [app0, app3, app4, app2]],
-    )
-
-    assert raiden_state_changes_search_for_item(
-        raiden=app0, item_type=ReceiveTransferCancelRoute, attributes={}
-    )
-    assert raiden_events_search_for_item(raiden=app0, item_type=EventRouteFailed, attributes={})
diff --git a/raiden/tests/integration/test_regression_parity.py b/raiden/tests/integration/test_regression_parity.py
index a913a56b80..cb3dbe9a4d 100644
--- a/raiden/tests/integration/test_regression_parity.py
+++ b/raiden/tests/integration/test_regression_parity.py
@@ -58,6 +58,7 @@ def test_locksroot_loading_during_channel_settle_handling(
         amount=PaymentAmount(10),
         identifier=PaymentID(1),
         transfer_state=TransferState.SECRET_NOT_REQUESTED,
+        routes=[[app0, app1]],
     )
     transfer(
         initiator_app=app1,
@@ -66,6 +67,7 @@ def test_locksroot_loading_during_channel_settle_handling(
         amount=PaymentAmount(7),
         identifier=PaymentID(2),
         transfer_state=TransferState.SECRET_NOT_REQUESTED,
+        routes=[[app1, app0]],
     )
 
     token_network_address = views.get_token_network_address_by_token_address(
diff --git a/raiden/tests/integration/test_send_queued_messages.py b/raiden/tests/integration/test_send_queued_messages.py
index d4ad7abbe0..d1e72bb465 100644
--- a/raiden/tests/integration/test_send_queued_messages.py
+++ b/raiden/tests/integration/test_send_queued_messages.py
@@ -157,11 +157,10 @@ def test_send_queued_messages_after_restart(  # pylint: disable=unused-argument
 
 
 @raise_on_failure
-@pytest.mark.skip(reason="Test is still using presence / health check")
 @pytest.mark.parametrize("number_of_nodes", [2])
 @pytest.mark.parametrize("channels_per_node", [1])
 @pytest.mark.parametrize("number_of_tokens", [1])
-def test_payment_statuses_are_restored(  # pylint: disable=unused-argument
+def test_payment_statuses_are_restored(
     raiden_network: List[RaidenService],
     restart_node: RestartNode,
     token_addresses: List[TokenAddress],
@@ -208,6 +207,7 @@ def test_payment_statuses_are_restored(  # pylint: disable=unused-argument
             target=target_address,
             identifier=PaymentID(identifier),
             secret=secret,
+            route_states=[create_route_state_for_route([app0, app1], token_address)],
         )
         assert payment_status.payment_identifier == identifier
 
@@ -246,8 +246,6 @@ def test_payment_statuses_are_restored(  # pylint: disable=unused-argument
     restart_node(app1)  # now that our checks are done start app1 again
 
     with watch_for_unlock_failures(*raiden_network):
-        waiting.wait_for_healthy(app0_restart, app1.address, network_wait)
-
         with gevent.Timeout(60):
             waiting.wait_for_payment_balance(
                 raiden=app1,
diff --git a/raiden/tests/integration/transfer/test_refundtransfer.py b/raiden/tests/integration/transfer/test_refundtransfer.py
deleted file mode 100644
index c19a19b181..0000000000
--- a/raiden/tests/integration/transfer/test_refundtransfer.py
+++ /dev/null
@@ -1,735 +0,0 @@
-import gevent
-import pytest
-
-from raiden.api.python import RaidenAPI
-from raiden.constants import BLOCK_ID_LATEST
-from raiden.raiden_service import RaidenService
-from raiden.settings import INTERNAL_ROUTING_DEFAULT_FEE_PERC
-from raiden.storage.sqlite import RANGE_ALL_STATE_CHANGES
-from raiden.tests.utils.detect_failure import raise_on_failure
-from raiden.tests.utils.events import (
-    raiden_events_search_for_item,
-    search_for_item,
-    wait_for_raiden_event,
-    wait_for_state_change,
-)
-from raiden.tests.utils.network import CHAIN
-from raiden.tests.utils.protocol import dont_handle_lock_expired_mock
-from raiden.tests.utils.transfer import (
-    assert_succeeding_transfer_invariants,
-    assert_synced_channel_state,
-    block_offset_timeout,
-    calculate_fee_for_amount,
-    create_route_state_for_route,
-    get_channelstate,
-    transfer,
-    wait_assert,
-)
-from raiden.transfer import channel, views
-from raiden.transfer.events import ContractSendChannelUpdateTransfer, EventPaymentSentFailed
-from raiden.transfer.mediated_transfer.events import (
-    SendLockedTransfer,
-    SendLockExpired,
-    SendRefundTransfer,
-)
-from raiden.transfer.mediated_transfer.initiator import calculate_fee_margin
-from raiden.transfer.mediated_transfer.state_change import ReceiveLockExpired
-from raiden.transfer.state_change import ContractReceiveChannelBatchUnlock, ReceiveProcessed
-from raiden.transfer.views import state_from_raiden
-from raiden.utils.typing import (
-    BlockNumber,
-    FeeAmount,
-    List,
-    PaymentAmount,
-    PaymentID,
-    TargetAddress,
-)
-from raiden.waiting import wait_for_block, wait_for_settle
-
-
-@raise_on_failure
-@pytest.mark.parametrize("channels_per_node", [CHAIN])
-@pytest.mark.parametrize("number_of_nodes", [3])
-@pytest.mark.parametrize("settle_timeout", [50])
-def test_refund_messages(raiden_chain: List[RaidenService], token_addresses, deposit):
-    # The network has the following topology:
-    #
-    #   App0 <---> App1 <---> App2
-    app0, app1, app2 = raiden_chain  # pylint: disable=unbalanced-tuple-unpacking
-    token_address = token_addresses[0]
-    token_network_registry_address = app0.default_registry.address
-
-    token_network_address = views.get_token_network_address_by_token_address(
-        views.state_from_raiden(app0), token_network_registry_address, token_address
-    )
-    assert token_network_address, "token_address must be registered by the fixtures."
-
-    identifier = PaymentID(1)
-
-    # Exhaust the channel App1 <-> App2 (to force the refund transfer)
-    # Here we make a single-hop transfer, no fees are charged so we should
-    # send the whole deposit amount to drain the channel.
-    transfer(
-        initiator_app=app1,
-        target_app=app2,
-        token_address=token_address,
-        amount=PaymentAmount(deposit),
-        identifier=identifier,
-        routes=[[app1, app2]],
-    )
-
-    refund_amount = deposit // 2
-    refund_fees = calculate_fee_for_amount(refund_amount)
-    fee_margin = calculate_fee_margin(refund_amount, refund_fees)
-    refund_amount_with_fees = refund_amount + refund_fees + fee_margin
-
-    payment_status = app0.mediated_transfer_async(
-        token_network_address=token_network_address,
-        amount=refund_amount,
-        target=TargetAddress(app2.address),
-        identifier=identifier,
-        route_states=[
-            create_route_state_for_route(
-                apps=raiden_chain,
-                token_address=token_address,
-                fee_estimate=FeeAmount(round(INTERNAL_ROUTING_DEFAULT_FEE_PERC * refund_amount)),
-            )
-        ],
-    )
-    msg = "Must fail, there are no routes available"
-    assert isinstance(payment_status.payment_done.wait(), EventPaymentSentFailed), msg
-
-    # The transfer from app0 to app2 failed, so the balances did change.
-    # Since the refund is not unlocked both channels have the corresponding
-    # amount locked (issue #1091)
-    send_lockedtransfer = raiden_events_search_for_item(
-        app0,
-        SendLockedTransfer,
-        {"transfer": {"lock": {"amount": refund_amount_with_fees}}},
-    )
-    assert send_lockedtransfer
-
-    send_refundtransfer = raiden_events_search_for_item(app1, SendRefundTransfer, {})
-    assert send_refundtransfer
-
-    with block_offset_timeout(app1):
-        wait_assert(
-            func=assert_synced_channel_state,
-            token_network_address=token_network_address,
-            app0=app0,
-            balance0=deposit,
-            pending_locks0=[send_lockedtransfer.transfer.lock],
-            app1=app1,
-            balance1=deposit,
-            pending_locks1=[send_refundtransfer.transfer.lock],
-        )
-        # This channel was exhausted to force the refund transfer except for the fees
-        wait_assert(
-            func=assert_succeeding_transfer_invariants,
-            token_network_address=token_network_address,
-            app0=app1,
-            balance0=0,
-            pending_locks0=[],
-            app1=app2,
-            balance1=deposit * 2,
-            pending_locks1=[],
-        )
-
-
-@raise_on_failure
-@pytest.mark.parametrize("privatekey_seed", ["test_refund_transfer:{}"])
-@pytest.mark.parametrize("number_of_nodes", [3])
-@pytest.mark.parametrize("channels_per_node", [CHAIN])
-def test_refund_transfer(
-    raiden_chain: List[RaidenService], token_addresses, deposit, retry_timeout
-):
-    """A failed transfer must send a refund back.
-
-    TODO:
-        - Unlock the token on refund #1091
-    """
-    # Topology:
-    #
-    #  0 -> 1 -> 2
-    #
-    app0, app1, app2 = raiden_chain
-    token_address = token_addresses[0]
-    token_network_registry_address = app0.default_registry.address
-
-    token_network_address = views.get_token_network_address_by_token_address(
-        views.state_from_raiden(app0), token_network_registry_address, token_address
-    )
-    assert token_network_address, "token_address must be registered by the fixtures."
-
-    # make a transfer to test the path app0 -> app1 -> app2
-    identifier_path = PaymentID(1)
-    amount_path = PaymentAmount(1)
-    with block_offset_timeout(app0):
-        transfer(
-            initiator_app=app0,
-            target_app=app2,
-            token_address=token_address,
-            amount=amount_path,
-            identifier=identifier_path,
-            routes=[[app0, app1, app2]],
-        )
-
-    # drain the channel app1 -> app2
-    identifier_drain = PaymentID(2)
-    amount_drain = PaymentAmount(deposit * 8 // 10)
-
-    transfer_timeout = block_offset_timeout(app1)
-    with transfer_timeout:
-        transfer(
-            initiator_app=app1,
-            target_app=app2,
-            token_address=token_address,
-            amount=amount_drain,
-            identifier=identifier_drain,
-            routes=[[app1, app2]],
-        )
-        wait_assert(
-            assert_synced_channel_state,
-            token_network_address,
-            app0,
-            deposit - amount_path,
-            [],
-            app1,
-            deposit + amount_path,
-            [],
-        )
-        wait_assert(
-            assert_synced_channel_state,
-            token_network_address,
-            app1,
-            deposit - amount_path - amount_drain,
-            [],
-            app2,
-            deposit + amount_path + amount_drain,
-            [],
-        )
-
-    # app0 -> app1 -> app2 is the only available path, but the channel app1 ->
-    # app2 doesn't have capacity, so a refund will be sent on app1 -> app0
-    identifier_refund = PaymentID(3)
-    amount_refund = PaymentAmount(50)
-    fee = calculate_fee_for_amount(amount_refund)
-    fee_margin = calculate_fee_margin(amount_refund, fee)
-    amount_refund_with_fees = amount_refund + fee + fee_margin
-
-    payment_status = app0.mediated_transfer_async(
-        token_network_address=token_network_address,
-        amount=amount_refund,
-        target=TargetAddress(app2.address),
-        identifier=identifier_refund,
-        route_states=[
-            create_route_state_for_route(
-                apps=raiden_chain,
-                token_address=token_address,
-                fee_estimate=FeeAmount(round(INTERNAL_ROUTING_DEFAULT_FEE_PERC * amount_refund)),
-            )
-        ],
-    )
-    msg = "there is no path with capacity, the transfer must fail"
-    assert isinstance(payment_status.payment_done.wait(), EventPaymentSentFailed), msg
-
-    # A lock structure with the correct amount
-
-    send_locked = raiden_events_search_for_item(
-        app0,
-        SendLockedTransfer,
-        {"transfer": {"lock": {"amount": amount_refund_with_fees}}},
-    )
-    assert send_locked
-    secrethash = send_locked.transfer.lock.secrethash
-
-    send_refund = raiden_events_search_for_item(app1, SendRefundTransfer, {})
-    assert send_refund
-
-    lock = send_locked.transfer.lock
-    refund_lock = send_refund.transfer.lock
-    assert lock.amount == refund_lock.amount
-    assert lock.secrethash
-    assert lock.expiration
-    assert lock.secrethash == refund_lock.secrethash
-
-    # Both channels have the amount locked because of the refund message
-    with transfer_timeout:
-        wait_assert(
-            assert_synced_channel_state,
-            token_network_address,
-            app0,
-            deposit - amount_path,
-            [lock],
-            app1,
-            deposit + amount_path,
-            [refund_lock],
-        )
-        wait_assert(
-            assert_synced_channel_state,
-            token_network_address,
-            app1,
-            deposit - amount_path - amount_drain,
-            [],
-            app2,
-            deposit + amount_path + amount_drain,
-            [],
-        )
-
-    # Additional checks for LockExpired causing nonce mismatch after refund transfer:
-    # https://github.com/raiden-network/raiden/issues/3146#issuecomment-447378046
-    # At this point make sure that the initiator has not deleted the payment task
-    assert secrethash in state_from_raiden(app0).payment_mapping.secrethashes_to_task
-
-    # Wait for lock lock expiration but make sure app0 never processes LockExpired
-    with dont_handle_lock_expired_mock(app0):
-        wait_for_block(
-            raiden=app0,
-            block_number=BlockNumber(channel.get_sender_expiration_threshold(lock.expiration) + 1),
-            retry_timeout=retry_timeout,
-        )
-        # make sure that app0 still has the payment task for the secrethash
-        # https://github.com/raiden-network/raiden/issues/3183
-        assert secrethash in state_from_raiden(app0).payment_mapping.secrethashes_to_task
-
-        # make sure that app1 sent a lock expired message for the secrethash
-        send_lock_expired = raiden_events_search_for_item(
-            app1, SendLockExpired, {"secrethash": secrethash}
-        )
-        assert send_lock_expired
-
-        assert app0.wal, "test apps must be started by the fixtures."
-        state_changes = app0.wal.storage.get_statechanges_by_range(RANGE_ALL_STATE_CHANGES)
-
-        # make sure that app0 never got it
-        assert not search_for_item(state_changes, ReceiveLockExpired, {"secrethash": secrethash})
-
-    # Out of the handicapped app0 transport.
-    # Now wait till app0 receives and processes LockExpired
-    receive_lock_expired = wait_for_state_change(
-        app0, ReceiveLockExpired, {"secrethash": secrethash}, retry_timeout
-    )
-    # And also till app1 received the processed
-    wait_for_state_change(
-        app1,
-        ReceiveProcessed,
-        {"message_identifier": receive_lock_expired.message_identifier},
-        retry_timeout,
-    )
-
-    # make sure app1 queue has cleared the SendLockExpired
-    chain_state1 = views.state_from_raiden(app1)
-    queues1 = views.get_all_messagequeues(chain_state=chain_state1)
-    result = [
-        (queue_id, queue)
-        for queue_id, queue in queues1.items()
-        if queue_id.recipient == app0.address and queue
-    ]
-    assert not result
-
-    # and now wait for 1 more block so that the payment task can be deleted
-    wait_for_block(
-        raiden=app0,
-        block_number=BlockNumber(app0.get_block_number() + 1),
-        retry_timeout=retry_timeout,
-    )
-
-    # and since the lock expired message has been sent and processed then the
-    # payment task should have been deleted from both nodes
-    # https://github.com/raiden-network/raiden/issues/3183
-    assert secrethash not in state_from_raiden(app0).payment_mapping.secrethashes_to_task
-    assert secrethash not in state_from_raiden(app1).payment_mapping.secrethashes_to_task
-
-
-@raise_on_failure
-@pytest.mark.parametrize("privatekey_seed", ["test_different_view_of_last_bp_during_unlock:{}"])
-@pytest.mark.parametrize("number_of_nodes", [3])
-@pytest.mark.parametrize("channels_per_node", [CHAIN])
-def test_different_view_of_last_bp_during_unlock(
-    raiden_chain: List[RaidenService],
-    restart_node,
-    token_addresses,
-    deposit,
-    retry_timeout,
-    blockchain_type,
-):
-    """Test for https://github.com/raiden-network/raiden/issues/3196#issuecomment-449163888"""
-    # Topology:
-    #
-    #  0 -> 1 -> 2
-    #
-    app0, app1, app2 = raiden_chain
-    token_address = token_addresses[0]
-    token_network_registry_address = app0.default_registry.address
-    token_network_address = views.get_token_network_address_by_token_address(
-        views.state_from_raiden(app0), token_network_registry_address, token_address
-    )
-    assert token_network_address
-    token_proxy = app0.proxy_manager.token(token_address, BLOCK_ID_LATEST)
-    initial_balance0 = token_proxy.balance_of(app0.address)
-    initial_balance1 = token_proxy.balance_of(app1.address)
-
-    # make a transfer to test the path app0 -> app1 -> app2
-    identifier_path = PaymentID(1)
-    amount_path = PaymentAmount(1)
-    with block_offset_timeout(app0):
-        transfer(
-            initiator_app=app0,
-            target_app=app2,
-            token_address=token_address,
-            amount=amount_path,
-            identifier=identifier_path,
-            routes=[[app0, app1, app2]],
-        )
-
-    # drain the channel app1 -> app2
-    identifier_drain = PaymentID(2)
-    amount_drain = PaymentAmount(deposit * 8 // 10)
-    with block_offset_timeout(app1):
-        transfer(
-            initiator_app=app1,
-            target_app=app2,
-            token_address=token_address,
-            amount=amount_drain,
-            identifier=identifier_drain,
-            routes=[[app1, app2]],
-        )
-        wait_assert(
-            assert_synced_channel_state,
-            token_network_address,
-            app0,
-            deposit - amount_path,
-            [],
-            app1,
-            deposit + amount_path,
-            [],
-        )
-        wait_assert(
-            assert_synced_channel_state,
-            token_network_address,
-            app1,
-            deposit - amount_path - amount_drain,
-            [],
-            app2,
-            deposit + amount_path + amount_drain,
-            [],
-        )
-
-    # app0 -> app1 -> app2 is the only available path, but the channel app1 ->
-    # app2 doesn't have capacity, so a refund will be sent on app1 -> app0
-    identifier_refund = PaymentID(3)
-    amount_refund = PaymentAmount(50)
-    fee = calculate_fee_for_amount(amount_refund)
-    fee_margin = calculate_fee_margin(amount_refund, fee)
-    amount_refund_with_fees = amount_refund + fee + fee_margin
-
-    payment_status = app0.mediated_transfer_async(
-        token_network_address=token_network_address,
-        amount=amount_refund,
-        target=TargetAddress(app2.address),
-        identifier=identifier_refund,
-        route_states=[
-            create_route_state_for_route(
-                apps=raiden_chain,
-                token_address=token_address,
-                fee_estimate=FeeAmount(round(INTERNAL_ROUTING_DEFAULT_FEE_PERC * amount_refund)),
-            )
-        ],
-    )
-    msg = "there is no path with capacity, the transfer must fail"
-    assert isinstance(payment_status.payment_done.wait(), EventPaymentSentFailed), msg
-
-    # A lock structure with the correct amount
-
-    send_locked = raiden_events_search_for_item(
-        app0,
-        SendLockedTransfer,
-        {"transfer": {"lock": {"amount": amount_refund_with_fees}}},
-    )
-    assert send_locked
-    secrethash = send_locked.transfer.lock.secrethash
-
-    send_refund = raiden_events_search_for_item(app1, SendRefundTransfer, {})
-    assert send_refund
-
-    lock = send_locked.transfer.lock
-    refund_lock = send_refund.transfer.lock
-    assert lock.amount == refund_lock.amount
-    assert lock.secrethash
-    assert lock.expiration
-    assert lock.secrethash == refund_lock.secrethash
-
-    # Both channels have the amount locked because of the refund message
-    with block_offset_timeout(app0):
-        wait_assert(
-            assert_synced_channel_state,
-            token_network_address,
-            app0,
-            deposit - amount_path,
-            [lock],
-            app1,
-            deposit + amount_path,
-            [refund_lock],
-        )
-        wait_assert(
-            assert_synced_channel_state,
-            token_network_address,
-            app1,
-            deposit - amount_path - amount_drain,
-            [],
-            app2,
-            deposit + amount_path + amount_drain,
-            [],
-        )
-
-    # Additional checks for LockExpired causing nonce mismatch after refund transfer:
-    # https://github.com/raiden-network/raiden/issues/3146#issuecomment-447378046
-    # At this point make sure that the initiator has not deleted the payment task
-    assert secrethash in state_from_raiden(app0).payment_mapping.secrethashes_to_task
-
-    # now app1 goes offline
-    app1.stop()
-    app1.greenlet.get()
-    assert not app1
-
-    # Wait for lock expiration so that app0 sends a LockExpired
-    wait_for_block(
-        raiden=app0,
-        block_number=BlockNumber(channel.get_sender_expiration_threshold(lock.expiration) + 1),
-        retry_timeout=retry_timeout,
-    )
-
-    # make sure that app0 sent a lock expired message for the secrethash
-    wait_for_raiden_event(app0, SendLockExpired, {"secrethash": secrethash}, retry_timeout)
-
-    # now app0 closes the channel
-    RaidenAPI(app0).channel_close(
-        registry_address=token_network_registry_address,
-        token_address=token_address,
-        partner_address=app1.address,
-    )
-
-    count = 0
-    on_raiden_events_original = app1.raiden_event_handler.on_raiden_events
-
-    def patched_on_raiden_events(raiden, chain_state, events):
-        nonlocal count
-
-        count += sum(1 for event in events if type(event) == ContractSendChannelUpdateTransfer)
-
-        on_raiden_events_original(raiden, chain_state, events)
-
-    setattr(app1.raiden_event_handler, "on_raiden_events", patched_on_raiden_events)  # NOQA
-
-    # and now app1 comes back online
-    restart_node(app1)
-    # test for https://github.com/raiden-network/raiden/issues/3216
-    assert count == 1, "Update transfer should have only been called once during restart"
-    channel_identifier = get_channelstate(app0, app1, token_network_address).identifier
-
-    # and we wait for settlement
-    wait_for_settle(
-        raiden=app0,
-        token_network_registry_address=token_network_registry_address,
-        token_address=token_address,
-        channel_ids=[channel_identifier],
-        retry_timeout=app0.alarm.sleep_time,
-    )
-
-    timeout = 30 if blockchain_type == "parity" else 10
-    with gevent.Timeout(timeout):
-        unlock_app0 = wait_for_state_change(
-            app0,
-            ContractReceiveChannelBatchUnlock,
-            {"receiver": app0.address},
-            retry_timeout,
-        )
-    assert unlock_app0
-    assert unlock_app0.returned_tokens == amount_refund_with_fees
-    with gevent.Timeout(timeout):
-        unlock_app1 = wait_for_state_change(
-            app1,
-            ContractReceiveChannelBatchUnlock,
-            {"receiver": app1.address},
-            retry_timeout,
-        )
-    assert unlock_app1
-    assert unlock_app1.returned_tokens == amount_refund_with_fees
-    final_balance0 = token_proxy.balance_of(app0.address)
-    final_balance1 = token_proxy.balance_of(app1.address)
-
-    assert final_balance0 - deposit - initial_balance0 == -1
-    assert final_balance1 - deposit - initial_balance1 == 1
-
-
-@raise_on_failure
-@pytest.mark.parametrize("privatekey_seed", ["test_refund_transfer:{}"])
-@pytest.mark.parametrize("number_of_nodes", [4])
-@pytest.mark.parametrize("number_of_tokens", [1])
-@pytest.mark.parametrize("channels_per_node", [CHAIN])
-def test_refund_transfer_after_2nd_hop(
-    raiden_chain: List[RaidenService], number_of_nodes, token_addresses, deposit, network_wait
-):
-    """Test the refund transfer sent due to failure after 2nd hop"""
-    # Topology:
-    #
-    #  0 -> 1 -> 2 -> 3
-    #
-    app0, app1, app2, app3 = raiden_chain
-    token_address = token_addresses[0]
-    token_network_registry_address = app0.default_registry.address
-
-    token_network_address = views.get_token_network_address_by_token_address(
-        views.state_from_raiden(app0), token_network_registry_address, token_address
-    )
-    assert token_network_address, "token_address must be registered by the fixtures."
-
-    # make a transfer to test the path app0 -> app1 -> app2 -> app3
-    identifier_path = PaymentID(1)
-    amount_path = PaymentAmount(1)
-    with block_offset_timeout(app0):
-        transfer(
-            initiator_app=app0,
-            target_app=app3,
-            token_address=token_address,
-            amount=amount_path,
-            identifier=identifier_path,
-            timeout=network_wait * number_of_nodes,
-            routes=[[app0, app1, app2, app3]],
-        )
-
-    # drain the channel app2 -> app3
-    identifier_drain = PaymentID(2)
-    amount_drain = PaymentAmount(deposit * 8 // 10)
-    with block_offset_timeout(app2):
-        transfer(
-            initiator_app=app2,
-            target_app=app3,
-            token_address=token_address,
-            amount=amount_drain,
-            identifier=identifier_drain,
-            timeout=network_wait,
-            routes=[[app2, app3]],
-        )
-        wait_assert(
-            assert_synced_channel_state,
-            token_network_address,
-            app0,
-            deposit - amount_path,
-            [],
-            app1,
-            deposit + amount_path,
-            [],
-        )
-        wait_assert(
-            assert_synced_channel_state,
-            token_network_address,
-            app1,
-            deposit - amount_path,
-            [],
-            app2,
-            deposit + amount_path,
-            [],
-        )
-        wait_assert(
-            assert_synced_channel_state,
-            token_network_address,
-            app2,
-            deposit - amount_path - amount_drain,
-            [],
-            app3,
-            deposit + amount_path + amount_drain,
-            [],
-        )
-
-    # app0 -> app1 -> app2 > app3 is the only available path, but the channel
-    # app2 -> app3 doesn't have capacity, so a refund will be sent on
-    # app2 -> app1 -> app0
-    identifier_refund = PaymentID(3)
-    amount_refund = PaymentAmount(50)
-    fee = calculate_fee_for_amount(amount_refund)
-    fee_margin = calculate_fee_margin(amount_refund, fee)
-    amount_refund_with_fees = amount_refund + fee + fee_margin
-
-    payment_status = app0.mediated_transfer_async(
-        token_network_address=token_network_address,
-        amount=amount_refund,
-        target=TargetAddress(app3.address),
-        identifier=identifier_refund,
-        route_states=[
-            create_route_state_for_route(
-                apps=[app0, app1, app2],
-                token_address=token_address,
-                fee_estimate=FeeAmount(round(INTERNAL_ROUTING_DEFAULT_FEE_PERC * amount_refund)),
-            )
-        ],
-    )
-    msg = "there is no path with capacity, the transfer must fail"
-    assert isinstance(payment_status.payment_done.wait(), EventPaymentSentFailed), msg
-
-    # Lock structures with the correct amount
-
-    send_locked1 = raiden_events_search_for_item(
-        app0,
-        SendLockedTransfer,
-        {"transfer": {"lock": {"amount": amount_refund_with_fees}}},
-    )
-    assert send_locked1
-
-    send_refund1 = raiden_events_search_for_item(app1, SendRefundTransfer, {})
-    assert send_refund1
-
-    lock1 = send_locked1.transfer.lock
-    refund_lock1 = send_refund1.transfer.lock
-    assert lock1.amount == refund_lock1.amount
-    assert lock1.secrethash == refund_lock1.secrethash
-
-    send_locked2 = raiden_events_search_for_item(
-        app1,
-        SendLockedTransfer,
-        {"transfer": {"lock": {"amount": amount_refund_with_fees}}},
-    )
-    assert send_locked2
-
-    send_refund2 = raiden_events_search_for_item(app2, SendRefundTransfer, {})
-    assert send_refund2
-
-    lock2 = send_locked2.transfer.lock
-    refund_lock2 = send_refund2.transfer.lock
-    assert lock2.amount == refund_lock2.amount
-    assert lock2.secrethash
-    assert lock2.expiration
-
-    # channels have the amount locked because of the refund message
-
-    with block_offset_timeout(app0):
-        wait_assert(
-            assert_synced_channel_state,
-            token_network_address,
-            app0,
-            deposit - amount_path,
-            [lock1],
-            app1,
-            deposit + amount_path,
-            [refund_lock1],
-        )
-        wait_assert(
-            assert_synced_channel_state,
-            token_network_address,
-            app1,
-            deposit - amount_path,
-            [lock2],
-            app2,
-            deposit + amount_path,
-            [refund_lock2],
-        )
-        wait_assert(
-            assert_synced_channel_state,
-            token_network_address,
-            app2,
-            deposit - amount_path - amount_drain,
-            [],
-            app3,
-            deposit + amount_path + amount_drain,
-            [],
-        )
diff --git a/raiden/tests/mocked/test_matrix_transport.py b/raiden/tests/mocked/test_matrix_transport.py
index bad1d3193f..ee68de3cbd 100644
--- a/raiden/tests/mocked/test_matrix_transport.py
+++ b/raiden/tests/mocked/test_matrix_transport.py
@@ -1,15 +1,16 @@
+from collections import defaultdict
 from typing import List  # pylint: skip-file  # XXX-UAM remove after tests are fixed
 
 import gevent
 import pytest
 from eth_utils import encode_hex
 from gevent import Timeout
+from gevent.event import Event
 from matrix_client.errors import MatrixRequestError
 from matrix_client.user import User
 
-from raiden.constants import EMPTY_SIGNATURE, Environment, MatrixMessageType
+from raiden.constants import Environment, MatrixMessageType
 from raiden.exceptions import RaidenUnrecoverableError
-from raiden.messages.abstract import Message
 from raiden.messages.transfers import SecretRequest
 from raiden.network.transport import MatrixTransport
 from raiden.network.transport.matrix import AddressReachability
@@ -21,12 +22,22 @@
 from raiden.tests.utils.factories import make_message_identifier, make_signer
 from raiden.tests.utils.mocks import MockRaidenService
 from raiden.transfer.identifiers import CANONICAL_IDENTIFIER_UNORDERED_QUEUE, QueueIdentifier
+from raiden.transfer.mediated_transfer.events import SendSecretRequest
 from raiden.utils.formatting import to_hex_address
 from raiden.utils.signer import LocalSigner
-from raiden.utils.typing import Address, AddressMetadata, BlockExpiration, PaymentAmount, PaymentID
+from raiden.utils.typing import (
+    Address,
+    AddressMetadata,
+    Any,
+    BlockExpiration,
+    Optional,
+    PaymentAmount,
+    PaymentID,
+    UserID,
+)
 
-USERID0 = "@0x1234567890123456789012345678901234567890:RestaurantAtTheEndOfTheUniverse"
-USERID1 = f"@{to_hex_address(factories.HOP1)}:Wonderland"  # pylint: disable=no-member
+USERID0 = UserID("@0x1234567890123456789012345678901234567890:RestaurantAtTheEndOfTheUniverse")
+USERID1 = UserID(f"@{to_hex_address(factories.HOP1)}:Wonderland")  # pylint: disable=no-member
 
 
 @pytest.fixture()
@@ -73,11 +84,10 @@ def mock_matrix(
     from raiden.network.transport.matrix.utils import UserPresence
 
     def make_client_monkey(
-        handle_messages_callback, handle_member_join_callback, servers, *args, **kwargs
+        handle_messages_callback, servers, *args, **kwargs
     ):  # pylint: disable=unused-argument
         return GMatrixClient(
             handle_messages_callback=handle_messages_callback,
-            handle_member_join_callback=handle_member_join_callback,
             base_url=servers[0],
         )
 
@@ -93,7 +103,6 @@ def mock_get_user_presence(self, user_id: str):  # pylint: disable=unused-argume
         return UserPresence.ONLINE
 
     config = MatrixTransportConfig(
-        broadcast_rooms=[],
         retries_before_backoff=retries_before_backoff,
         retry_interval_initial=retry_interval_initial,
         retry_interval_max=retry_interval_max,
@@ -329,15 +338,29 @@ def send_raw(
     del mock_matrix.sent_messages
 
 
-def make_message(sign: bool = True) -> Message:
-    message = SecretRequest(
+def make_message_event(
+    recipient: Address,
+    address_metadata: Any = None,
+    canonical_identifier=CANONICAL_IDENTIFIER_UNORDERED_QUEUE,
+):
+    return SendSecretRequest(
+        recipient=recipient,
+        recipient_metadata=address_metadata,
+        canonical_identifier=canonical_identifier,
         message_identifier=make_message_identifier(),
         payment_identifier=PaymentID(1),
-        secrethash=factories.UNIT_SECRETHASH,
         amount=PaymentAmount(1),
         expiration=BlockExpiration(10),
-        signature=EMPTY_SIGNATURE,
+        secrethash=factories.UNIT_SECRETHASH,
     )
+
+
+def make_message(sign: bool = True, message_event: Optional[SendSecretRequest] = None):
+    if message_event is None:
+        # recipient etc. doesn't matter, since this will not be considered
+        # on converting to a message
+        message_event = make_message_event(Address(factories.HOP1))
+    message = SecretRequest.from_event(message_event)
     if sign:
         message.sign(LocalSigner(factories.HOP1_KEY))
     return message
@@ -415,6 +438,123 @@ def test_message_in_p2p_room_raises(  # pylint: disable=unused-argument
         mock_matrix._handle_sync_messages([(room, [event])])
 
 
+def test_retry_queue_batch_by_user_id(mock_matrix: MatrixTransport) -> None:
+    """
+    Make sure that the retry-queues batches messages by the attached
+    AddressMetadata, and pass the message-data to the _send_raw method per batch.
+    """
+
+    original_send_raw = mock_matrix._send_raw
+    sent_messages_by_callcount = defaultdict(list)
+    sent_messages = list()
+    send_raw_call_count = 0
+
+    def send_raw(
+        receiver_address: Address,
+        data: str,
+        message_type: MatrixMessageType = MatrixMessageType.TEXT,
+        receiver_metadata: AddressMetadata = None,
+    ) -> None:
+        nonlocal send_raw_call_count
+        send_raw_call_count += 1
+        for message in data.split("\n"):
+            sent_messages.append(message)
+            sent_messages_by_callcount[send_raw_call_count].append((message, receiver_metadata))
+
+    mock_matrix._send_raw = send_raw  # type: ignore
+
+    # Pretend the Transport greenlet is running
+    mock_matrix.greenlet = True
+
+    receiver_address = Address(factories.HOP1)
+
+    # This is intentionally not using ``MatrixTransport._get_retrier()`` since we don't want the
+    # greenlet to run but instead manually call its `_check_and_send()` method.
+    retry_queue = _RetryQueue(transport=mock_matrix, receiver=receiver_address)
+
+    # enqueue first message with no address metadata
+    address_metadata1 = None
+    message_event1 = make_message_event(receiver_address, address_metadata=address_metadata1)
+    message1 = make_message(message_event=message_event1)
+    serialized_message1 = MessageSerializer.serialize(message1)
+    retry_queue.enqueue(message_event1.queue_identifier, [(message1, address_metadata1)])
+
+    # enqueue second message with address metadata dict,
+    # but no "user_id" key - this will end up in the same batch as address_metadata=None
+    address_metadata2: AddressMetadata = {"no_user_id": UserID("@nothing:here")}
+
+    message_event2 = make_message_event(receiver_address, address_metadata=address_metadata2)
+    message2 = make_message(message_event=message_event2)
+    serialized_message2 = MessageSerializer.serialize(message2)
+    retry_queue.enqueue(message_event2.queue_identifier, [(message2, address_metadata2)])
+
+    # enqueue third message with correct address metadata
+    correct_address_metadata: AddressMetadata = {"user_id": USERID1}
+    message_event3 = make_message_event(
+        receiver_address, address_metadata=correct_address_metadata
+    )
+    message3 = make_message(message_event=message_event3)
+    serialized_message3 = MessageSerializer.serialize(message3)
+    retry_queue.enqueue(message_event3.queue_identifier, [(message3, correct_address_metadata)])
+
+    # enqueue another message with correct address metadata
+    message_event4 = make_message_event(
+        receiver_address, address_metadata=correct_address_metadata
+    )
+    message4 = make_message(message_event=message_event4)
+    serialized_message4 = MessageSerializer.serialize(message4)
+    retry_queue.enqueue(message_event4.queue_identifier, [(message4, correct_address_metadata)])
+
+    # enqueue a message with address metadata dict,
+    # that has key `user_id`, but is not a "valid" user-id. This should still get passed to the
+    # send raw
+    address_metadata5: AddressMetadata = {"user_id": UserID("invalid")}
+    message_event5 = make_message_event(receiver_address, address_metadata=address_metadata5)
+    message5 = make_message(message_event=message_event5)
+    serialized_message5 = MessageSerializer.serialize(message5)
+    retry_queue.enqueue(message_event5.queue_identifier, [(message5, address_metadata5)])
+
+    # Pretend the Transport greenlet is running
+    mock_matrix.greenlet = True
+
+    mock_matrix._queueids_to_queues[message_event1.queue_identifier] = [
+        message_event1,
+        message_event2,
+        message_event3,
+        message_event4,
+        message_event5,
+    ]
+
+    retry_queue._check_and_send()
+
+    assert len(sent_messages) == 5
+    # only 3 batches expected by "user_id" in AddressMetadata
+    # so transport._send_raw should also only be called 3 times
+    assert send_raw_call_count == 3
+
+    # for the first batch, _send_raw will be called with address_metadata=None, because for
+    # message2, the address-metadata dict didn't include a "user_id" key
+    batch1 = [(serialized_message1, None), (serialized_message2, None)]
+    batch2 = [
+        (serialized_message3, correct_address_metadata),
+        (serialized_message4, correct_address_metadata),
+    ]
+    batch3 = [(serialized_message5, address_metadata5)]
+
+    for sent_batch in sent_messages_by_callcount.values():
+        matches = False
+        for batch in [batch1, batch2, batch3]:
+            if all(elem in sent_batch for elem in batch):  # type: ignore
+                matches = True
+                break
+        msg = f"Sent batch not expected: {sent_batch}"
+        assert matches is True, msg
+
+    mock_matrix._queueids_to_queues[message_event1.queue_identifier].clear()
+
+    mock_matrix._send_raw = original_send_raw  # type: ignore
+
+
 @pytest.mark.parametrize("retry_interval_initial", [0.01])
 @pytest.mark.usefixtures("record_sent_messages", "all_peers_reachable")
 def test_retry_queue_does_not_resend_removed_messages(
@@ -436,38 +576,33 @@ def test_retry_queue_does_not_resend_removed_messages(
     # greenlet to run but instead manually call its `_check_and_send()` method.
     retry_queue = _RetryQueue(transport=mock_matrix, receiver=Address(factories.HOP1))
 
-    message = make_message()
+    message_event = make_message_event(recipient=Address(factories.HOP1))
+    message = make_message(message_event=message_event)
     serialized_message = MessageSerializer.serialize(message)
-    queue_identifier = QueueIdentifier(
-        recipient=Address(factories.HOP1),
-        canonical_identifier=CANONICAL_IDENTIFIER_UNORDERED_QUEUE,
-    )
-    retry_queue.enqueue(queue_identifier, [(message, None)])
 
-    # TODO: Fix the code below, the types are not matching.
-    mock_matrix._queueids_to_queues[queue_identifier] = [message]  # type: ignore
+    retry_queue.enqueue(message_event.queue_identifier, [(message, None)])
+
+    mock_matrix._queueids_to_queues[message_event.queue_identifier] = [message_event]
 
-    with retry_queue._lock:
-        retry_queue._check_and_send()
+    retry_queue._check_and_send()
 
     assert len(mock_matrix.sent_messages) == 1  # type: ignore
     assert (factories.HOP1, serialized_message) in mock_matrix.sent_messages  # type: ignore
 
-    mock_matrix._queueids_to_queues[queue_identifier].clear()
+    mock_matrix._queueids_to_queues[message_event.queue_identifier].clear()
 
     # Make sure the retry interval has elapsed
     gevent.sleep(retry_interval_initial * 5)
 
-    with retry_queue._lock:
-        # The message has been removed from the raiden queue and should therefore not be sent again
-        retry_queue._check_and_send()
+    # The message has been removed from the raiden queue and should therefore not be sent again
+    retry_queue._check_and_send()
 
     assert len(mock_matrix.sent_messages) == 1  # type: ignore
 
 
 @pytest.mark.parametrize("retry_interval_initial", [0.05])
 def test_retryqueue_idle_terminate(mock_matrix: MatrixTransport, retry_interval_initial: float):
-    """ Ensure ``RetryQueue``s exit if they are idle for too long. """
+    """Ensure ``RetryQueue``s exit if they are idle for too long."""
     retry_queue = mock_matrix._get_retrier(Address(factories.HOP1))
     idle_after = RETRY_QUEUE_IDLE_AFTER * retry_interval_initial
 
@@ -490,7 +625,7 @@ def test_retryqueue_idle_terminate(mock_matrix: MatrixTransport, retry_interval_
 def test_retryqueue_not_idle_with_messages(
     mock_matrix: MatrixTransport, retry_interval_initial: float
 ) -> None:
-    """ Ensure ``RetryQueue``s don't become idle while messages remain in the internal queue. """
+    """Ensure ``RetryQueue``s don't become idle while messages remain in the internal queue."""
     retry_queue = mock_matrix._get_retrier(Address(factories.HOP1))
     idle_after = RETRY_QUEUE_IDLE_AFTER * retry_interval_initial
 
@@ -513,3 +648,55 @@ def test_retryqueue_not_idle_with_messages(
     retry_queue_2 = mock_matrix._get_retrier(Address(factories.HOP1))
     # The first queue has never become idle, therefore the same object must be returned
     assert retry_queue is retry_queue_2
+
+
+def test_retryqueue_enqueue_not_blocking(
+    mock_matrix: MatrixTransport, retry_interval_initial: float, monkeypatch
+) -> None:
+    """Ensure ``RetryQueue``s don't become idle while messages remain in the internal queue."""
+    # Pretend the Transport greenlet is running
+    mock_matrix.greenlet = True
+
+    lock = Event()
+    call_count = 0
+
+    def send_raw_blocking(
+        receiver_address: Address,
+        data: str,
+        message_type: MatrixMessageType = MatrixMessageType.TEXT,
+        receiver_metadata: AddressMetadata = None,
+    ):
+        nonlocal call_count
+        call_count += 1
+        lock.wait()
+
+    monkeypatch.setattr(mock_matrix, "_send_raw", send_raw_blocking)
+
+    retry_queue = mock_matrix._get_retrier(Address(factories.HOP1))
+
+    message_event = make_message_event(recipient=Address(factories.HOP1))
+    message = make_message(message_event=message_event)
+    mock_matrix._queueids_to_queues[message_event.queue_identifier] = [message_event]
+    retry_queue.enqueue(message_event.queue_identifier, [(message, None)])
+
+    gevent.sleep(0.01)
+
+    assert call_count == 1
+    assert len(retry_queue._message_queue) == 1
+
+    message_event = make_message_event(recipient=Address(factories.HOP1))
+    message = make_message(message_event=message_event)
+    # The timeout is to ensure that send_async immediately gives back control to the event loop
+    with gevent.Timeout(0, False):
+        try:
+            mock_matrix._queueids_to_queues[message_event.queue_identifier] = [message_event]
+            retry_queue.enqueue(message_event.queue_identifier, [(message, None)])
+        except gevent.Timeout:
+            raise AssertionError("send_async is blocking")
+
+    # release the lock and switch context
+    lock.set()
+    gevent.sleep(0.01)
+
+    assert call_count == 2
+    assert len(retry_queue._message_queue) == 1
diff --git a/raiden/tests/scenarios/bf1_basic_functionality.yaml b/raiden/tests/scenarios/bf1_basic_functionality.yaml
index d7840552fa..a74191f0ea 100644
--- a/raiden/tests/scenarios/bf1_basic_functionality.yaml
+++ b/raiden/tests/scenarios/bf1_basic_functionality.yaml
@@ -1,7 +1,7 @@
 version: 2
 
 settings:
-  gas_price: "fast"
+  gas_price: {{ gas_price }}
   services:
     pfs:
       url: "{{ pfs_with_fee }}"
diff --git a/raiden/tests/scenarios/bf2_long_running.yaml b/raiden/tests/scenarios/bf2_long_running.yaml
index 9d103708af..e4f6e2ee38 100644
--- a/raiden/tests/scenarios/bf2_long_running.yaml
+++ b/raiden/tests/scenarios/bf2_long_running.yaml
@@ -1,7 +1,7 @@
 version: 2
 
 settings:
-  gas_price: "fast"
+  gas_price: {{ gas_price }}
   services:
     pfs:
       url: {{ pfs_with_fee }}
diff --git a/raiden/tests/scenarios/bf3_multi_directional_payment.yaml b/raiden/tests/scenarios/bf3_multi_directional_payment.yaml
index f8b9ae79d9..dea953b735 100644
--- a/raiden/tests/scenarios/bf3_multi_directional_payment.yaml
+++ b/raiden/tests/scenarios/bf3_multi_directional_payment.yaml
@@ -1,7 +1,7 @@
 version: 2
 
 settings:
-  gas_price: "fast"
+  gas_price: {{ gas_price }}
   services:
     pfs:
       url: {{ pfs_with_fee }}
diff --git a/raiden/tests/scenarios/bf4_multi_payments_same_node.yaml b/raiden/tests/scenarios/bf4_multi_payments_same_node.yaml
index 72c9ff867e..b90a518bdf 100644
--- a/raiden/tests/scenarios/bf4_multi_payments_same_node.yaml
+++ b/raiden/tests/scenarios/bf4_multi_payments_same_node.yaml
@@ -1,7 +1,7 @@
 version: 2
 
 settings:
-  gas_price: "fast"
+  gas_price: {{ gas_price }}
   services:
     pfs:
       url: {{ pfs_with_fee }}
diff --git a/raiden/tests/scenarios/bf6_stress_hub_node.yaml b/raiden/tests/scenarios/bf6_stress_hub_node.yaml
index 85da27c033..c1bab22044 100644
--- a/raiden/tests/scenarios/bf6_stress_hub_node.yaml
+++ b/raiden/tests/scenarios/bf6_stress_hub_node.yaml
@@ -1,7 +1,7 @@
 version: 2
 
 settings:
-  gas_price: "fast"
+  gas_price: {{ gas_price }}
   services:
     pfs:
       url: {{ pfs_with_fee }}
@@ -42,7 +42,7 @@ nodes:
     3:
       matrix-server: {{ matrix_servers[3] }}
     4:
-      matrix-server: {{ matrix_servers[4] }}
+      matrix-server: {{ matrix_servers[3] }}
     5:
       matrix-server: {{ matrix_servers[0] }}
     6:
@@ -52,7 +52,7 @@ nodes:
     8:
       matrix-server: {{ matrix_servers[3] }}
     9:
-      matrix-server: {{ matrix_servers[4] }}
+      matrix-server: {{ matrix_servers[3] }}
 
 # This is the BF6 scenario. It sets up a tolopogy with 9 nodes connected to node0,
 # so that node0 is the single hub that all payments have to go through. First one
diff --git a/raiden/tests/scenarios/bf7_long_path.yaml b/raiden/tests/scenarios/bf7_long_path.yaml
index 081b3b5fb0..539ce16faf 100644
--- a/raiden/tests/scenarios/bf7_long_path.yaml
+++ b/raiden/tests/scenarios/bf7_long_path.yaml
@@ -2,7 +2,7 @@ version: 2
 
 settings:
   # Gas price to use, either `fast`, `medium` or an integer (in gwei)
-  gas_price: "fast"
+  gas_price: {{ gas_price }}
   services:
     pfs:
       url: {{ pfs_with_fee }}
diff --git a/raiden/tests/scenarios/mfee1_flat_fee.yaml b/raiden/tests/scenarios/mfee1_flat_fee.yaml
index 3841d52da3..9809f3e0cf 100644
--- a/raiden/tests/scenarios/mfee1_flat_fee.yaml
+++ b/raiden/tests/scenarios/mfee1_flat_fee.yaml
@@ -1,7 +1,7 @@
 version: 2
 
 settings:
-  gas_price: "fast"
+  gas_price: {{ gas_price }}
   services:
     pfs:
       url: {{ pfs_with_fee }}
diff --git a/raiden/tests/scenarios/mfee2_proportional_fees.yaml b/raiden/tests/scenarios/mfee2_proportional_fees.yaml
index ae10dbfe98..b829136e75 100644
--- a/raiden/tests/scenarios/mfee2_proportional_fees.yaml
+++ b/raiden/tests/scenarios/mfee2_proportional_fees.yaml
@@ -1,7 +1,7 @@
 version: 2
 
 settings:
-  gas_price: "fast"
+  gas_price: {{ gas_price }}
   services:
     pfs:
       url: {{ pfs_with_fee }}
diff --git a/raiden/tests/scenarios/mfee3_only_imbalance_fees.yaml b/raiden/tests/scenarios/mfee3_only_imbalance_fees.yaml
index ff4537aedd..9336f4465f 100644
--- a/raiden/tests/scenarios/mfee3_only_imbalance_fees.yaml
+++ b/raiden/tests/scenarios/mfee3_only_imbalance_fees.yaml
@@ -1,7 +1,7 @@
 version: 2
 
 settings:
-  gas_price: "fast"
+  gas_price: {{ gas_price }}
   services:
     pfs:
       url: {{ pfs_with_fee }}
diff --git a/raiden/tests/scenarios/mfee4_combined_fees.yaml b/raiden/tests/scenarios/mfee4_combined_fees.yaml
index e9f3410018..b3577d853e 100644
--- a/raiden/tests/scenarios/mfee4_combined_fees.yaml
+++ b/raiden/tests/scenarios/mfee4_combined_fees.yaml
@@ -1,7 +1,7 @@
 version: 2
 
 settings:
-  gas_price: "fast"
+  gas_price: {{ gas_price }}
   services:
     pfs:
       url: {{ pfs_with_fee }}
diff --git a/raiden/tests/scenarios/ms1_simple_monitoring.yaml b/raiden/tests/scenarios/ms1_simple_monitoring.yaml
index a6b196cfcc..6f4501f768 100644
--- a/raiden/tests/scenarios/ms1_simple_monitoring.yaml
+++ b/raiden/tests/scenarios/ms1_simple_monitoring.yaml
@@ -1,7 +1,7 @@
 version: 2
 
 settings:
-  gas_price: "fast"
+  gas_price: {{ gas_price }}
   # Adapt to chain used
   services:
     pfs:
diff --git a/raiden/tests/scenarios/ms1_simple_monitoring_snapshot.yaml b/raiden/tests/scenarios/ms1_simple_monitoring_snapshot.yaml
new file mode 100644
index 0000000000..f7e0922424
--- /dev/null
+++ b/raiden/tests/scenarios/ms1_simple_monitoring_snapshot.yaml
@@ -0,0 +1,72 @@
+version: 2
+
+settings:
+  gas_price: {{ gas_price }}
+  # Adapt to chain used
+  services:
+    pfs:
+      url: {{ pfs_with_fee }}
+    udc:
+      enable: true
+      token:
+        deposit: true
+        balance_per_node: {{ ms_reward_with_margin }}
+
+token:
+  address: "{{ transfer_token }}"
+  balance_fund: 10_000_000_000_000_000_000
+
+nodes:
+  reuse_accounts: true
+  restore_snapshot: true
+  count: 2
+  raiden_version: local
+
+  default_options:
+    gas-price: fast
+    enable-monitoring: true
+    proportional-fee:
+      - "{{ transfer_token }}"
+      - 0
+    proportional-imbalance-fee:
+      - "{{ transfer_token }}"
+      - 0
+    default-settle-timeout: {{ settlement_timeout_min }}
+    default-reveal-timeout: 20
+
+# This is the MS1 scenario. A channel between two nodes is opened, a transfer is made. Then, node1 goes offline
+# and node0 closes the channel. The last assert checks whether the monitoring service interferes and received its reward.
+
+scenario:
+  serial:
+    tasks:
+      - snapshot:
+          tasks:
+            - open_channel: {from: 0, to: 1, total_deposit: 2_000_000_000_000_000_000}
+      - transfer: {from: 0, to: 1, amount: 1_000_000_000_000_000_000, expected_http_status: 200}
+      ## Wait for Monitor Request to be sent
+      - wait_blocks: 1
+      - store_channel_info: {from: 0, to: 1, key: "MS Test Channel"}
+      - stop_node: 1
+      - close_channel: {from: 0, to: 1}
+      ## Wait for channel to be closed
+      - wait_blocks: 1
+      - assert: {from: 0, to: 1, total_deposit: 2_000_000_000_000_000_000, balance: 1_000_000_000_000_000_000, state: "closed"}
+      - assert_events:
+          contract_name: "TokenNetwork"
+          event_name: "ChannelClosed"
+          num_events: 1
+          event_args: {closing_participant: 0}
+
+      ## The MS reacts within the settle_timeout
+      - wait_blocks: {{ settlement_timeout_min }}
+      - assert_events:
+          contract_name: "TokenNetwork"
+          event_name: "NonClosingBalanceProofUpdated"
+          num_events: 1
+          event_args: {closing_participant: 0}
+
+      ## Monitored channel must be settled before the monitoring service can claim its reward
+      ## To make sure the transactions gets mined in time, 10 additional blocks are added
+      - wait_blocks: 10
+      - assert_ms_claim: {channel_info_key: "MS Test Channel"}
diff --git a/raiden/tests/scenarios/ms2_simple_monitoring.yaml b/raiden/tests/scenarios/ms2_simple_monitoring.yaml
index a72c172d0f..aa9b02c565 100644
--- a/raiden/tests/scenarios/ms2_simple_monitoring.yaml
+++ b/raiden/tests/scenarios/ms2_simple_monitoring.yaml
@@ -1,7 +1,7 @@
 version: 2
 
 settings:
-  gas_price: "fast"
+  gas_price: {{ gas_price }}
   # Adapt to chain used
   services:
     pfs:
diff --git a/raiden/tests/scenarios/ms3_simple_monitoring.yaml b/raiden/tests/scenarios/ms3_simple_monitoring.yaml
index 3699213ab9..67b3f6d4e5 100644
--- a/raiden/tests/scenarios/ms3_simple_monitoring.yaml
+++ b/raiden/tests/scenarios/ms3_simple_monitoring.yaml
@@ -1,7 +1,7 @@
 version: 2
 
 settings:
-  gas_price: "fast"
+  gas_price: {{ gas_price }}
   # Adapt to chain used
   services:
     pfs:
diff --git a/raiden/tests/scenarios/ms4_udc_too_low.yaml b/raiden/tests/scenarios/ms4_udc_too_low.yaml
index 1c534add49..b8e1cab54c 100644
--- a/raiden/tests/scenarios/ms4_udc_too_low.yaml
+++ b/raiden/tests/scenarios/ms4_udc_too_low.yaml
@@ -1,7 +1,7 @@
 version: 2
 
 settings:
-  gas_price: "fast"
+  gas_price: {{ gas_price }}
   # Adapt to chain used
   services:
     pfs:
diff --git a/raiden/tests/scenarios/pfs1_get_a_simple_path.yaml b/raiden/tests/scenarios/pfs1_get_a_simple_path.yaml
index 296597fc0b..586db93a89 100644
--- a/raiden/tests/scenarios/pfs1_get_a_simple_path.yaml
+++ b/raiden/tests/scenarios/pfs1_get_a_simple_path.yaml
@@ -1,7 +1,7 @@
 version: 2
 
 settings:
-  gas_price: "fast"
+  gas_price: {{ gas_price }}
   services:
     pfs:
       url: {{ pfs_with_fee }}
diff --git a/raiden/tests/scenarios/pfs2_simple_no_path.yaml b/raiden/tests/scenarios/pfs2_simple_no_path.yaml
index 7a0285333c..6bb62653ae 100644
--- a/raiden/tests/scenarios/pfs2_simple_no_path.yaml
+++ b/raiden/tests/scenarios/pfs2_simple_no_path.yaml
@@ -1,7 +1,7 @@
 version: 2
 
 settings:
-  gas_price: "fast"
+  gas_price: {{ gas_price }}
   services:
     pfs:
       url: {{ pfs_with_fee }}
diff --git a/raiden/tests/scenarios/pfs3_multiple_paths.yaml b/raiden/tests/scenarios/pfs3_multiple_paths.yaml
index 41f247c7a9..353eb250b3 100644
--- a/raiden/tests/scenarios/pfs3_multiple_paths.yaml
+++ b/raiden/tests/scenarios/pfs3_multiple_paths.yaml
@@ -1,7 +1,7 @@
 version: 2
 
 settings:
-  gas_price: "fast"
+  gas_price: {{ gas_price }}
   services:
     pfs:
       url: {{ pfs_with_fee }}
diff --git a/raiden/tests/scenarios/pfs4_use_best_path.yaml b/raiden/tests/scenarios/pfs4_use_best_path.yaml
index e73ac7b7da..2144e86e5d 100644
--- a/raiden/tests/scenarios/pfs4_use_best_path.yaml
+++ b/raiden/tests/scenarios/pfs4_use_best_path.yaml
@@ -1,7 +1,7 @@
 version: 2
 
 settings:
-  gas_price: "fast"
+  gas_price: {{ gas_price }}
   services:
     pfs:
       url: {{ pfs_with_fee }}
diff --git a/raiden/tests/scenarios/pfs5_too_low_capacity.yaml b/raiden/tests/scenarios/pfs5_too_low_capacity.yaml
index 082143acc2..44f446566a 100644
--- a/raiden/tests/scenarios/pfs5_too_low_capacity.yaml
+++ b/raiden/tests/scenarios/pfs5_too_low_capacity.yaml
@@ -1,7 +1,7 @@
 version: 2
 
 settings:
-  gas_price: "fast"
+  gas_price: {{ gas_price }}
   services:
     pfs:
       url: {{ pfs_with_fee }}
diff --git a/raiden/tests/scenarios/pfs6_simple_path_rewards.yaml b/raiden/tests/scenarios/pfs6_simple_path_rewards.yaml
index 2511b02abb..f29ee6d1fd 100644
--- a/raiden/tests/scenarios/pfs6_simple_path_rewards.yaml
+++ b/raiden/tests/scenarios/pfs6_simple_path_rewards.yaml
@@ -1,7 +1,7 @@
 version: 2
 
 settings:
-  gas_price: "fast"
+  gas_price: {{ gas_price }}
   services:
     pfs:
       url: {{ pfs_with_fee }}
diff --git a/raiden/tests/scenarios/pfs7_multiple_payments.yaml b/raiden/tests/scenarios/pfs7_multiple_payments.yaml
index 1998d71cd5..7871cf98b7 100644
--- a/raiden/tests/scenarios/pfs7_multiple_payments.yaml
+++ b/raiden/tests/scenarios/pfs7_multiple_payments.yaml
@@ -1,7 +1,7 @@
 version: 2
 
 settings:
-  gas_price: "fast"
+  gas_price: {{ gas_price }}
   services:
     pfs:
       url: {{ pfs_with_fee }}
diff --git a/raiden/tests/scenarios/pfs8_mediator_goes_offline.yaml b/raiden/tests/scenarios/pfs8_mediator_goes_offline.yaml
index aad079f94b..4fbdacd631 100644
--- a/raiden/tests/scenarios/pfs8_mediator_goes_offline.yaml
+++ b/raiden/tests/scenarios/pfs8_mediator_goes_offline.yaml
@@ -1,7 +1,7 @@
 version: 2
 
 settings:
-  gas_price: "fast"
+  gas_price: {{ gas_price }}
   services:
     pfs:
       url: {{ pfs_with_fee }}
diff --git a/raiden/tests/scenarios/pfs9_partial_withdraw.yaml b/raiden/tests/scenarios/pfs9_partial_withdraw.yaml
index d33eaa0644..aca5963c9c 100644
--- a/raiden/tests/scenarios/pfs9_partial_withdraw.yaml
+++ b/raiden/tests/scenarios/pfs9_partial_withdraw.yaml
@@ -1,7 +1,7 @@
 version: 2
 
 settings:
-  gas_price: "fast"
+  gas_price: {{ gas_price }}
   services:
     pfs:
       url: {{ pfs_with_fee }}
diff --git a/raiden/tests/unit/api/test_encoding.py b/raiden/tests/unit/api/test_encoding.py
index 737dc708b5..e30d0d9c80 100644
--- a/raiden/tests/unit/api/test_encoding.py
+++ b/raiden/tests/unit/api/test_encoding.py
@@ -2,6 +2,7 @@
 
 import pytest
 from marshmallow.fields import DateTime
+from werkzeug.routing import Map
 
 from raiden.api.v1.encoding import AddressField, BaseSchema, HexAddressConverter
 from raiden.utils.typing import Address
@@ -26,7 +27,7 @@ def test_timestamp_field():
 
 
 def test_hex_converter():
-    converter = HexAddressConverter(map=None)
+    converter = HexAddressConverter(map=Map())
 
     # invalid hex data
     with pytest.raises(Exception):
diff --git a/raiden/tests/unit/fixtures.py b/raiden/tests/unit/fixtures.py
index eaa3f7e759..04bce55f9f 100644
--- a/raiden/tests/unit/fixtures.py
+++ b/raiden/tests/unit/fixtures.py
@@ -12,8 +12,13 @@
 
 
 @pytest.fixture
-def our_address():
-    return factories.make_address()
+def our_signer():
+    return factories.make_signer()
+
+
+@pytest.fixture
+def our_address(our_signer):
+    return our_signer.address
 
 
 @pytest.fixture
@@ -113,6 +118,6 @@ def netting_channel_state(chain_state, token_network_state, token_network_regist
 
 @pytest.fixture
 def requests_responses() -> responses.RequestsMock:
-    """ Uses ``responses`` to provide easy requests tests. """
+    """Uses ``responses`` to provide easy requests tests."""
     with responses.RequestsMock() as mock_responses:
         yield mock_responses
diff --git a/raiden/tests/unit/serialized_messages/LockedTransfer.json b/raiden/tests/unit/serialized_messages/LockedTransfer.json
index 530500383c..fb2f259792 100644
--- a/raiden/tests/unit/serialized_messages/LockedTransfer.json
+++ b/raiden/tests/unit/serialized_messages/LockedTransfer.json
@@ -24,7 +24,7 @@
     "nonce": "1",
     "payment_identifier": "1",
     "recipient": "0x7461726765747461726765747461726765747461",
-    "signature": "0x997d411457b6f883d6e38c1c4c991eaae59e1f7cb7e64b251652d6c4dafb15363bc444ea2ece7418225349a28d3844f8d46b039799f2b00b365d2c9ddf9f26cf1b",
+    "signature": "0xa8c6aa0cbf6c6b8f8dab91d63c3860701693e1b4309550bace19a1ed6204622c5d84dddbe9aeeaaebd3acaf8ee9df765549f1ec109fdf78fa111dc1355ad79981b",
     "target": "0x7461726765747461726765747461726765747461",
     "token": "0x746f6b656e746f6b656e746f6b656e746f6b656e",
     "token_network_address": "0x6e6574776f726b6e6574776f726b6e6574776f72",
diff --git a/raiden/tests/unit/test_cli.py b/raiden/tests/unit/test_cli.py
index 80453a908b..956178689b 100644
--- a/raiden/tests/unit/test_cli.py
+++ b/raiden/tests/unit/test_cli.py
@@ -2,7 +2,7 @@
 from unittest.mock import patch
 
 import pytest
-from click.core import ParameterSource  # type: ignore
+from click.core import ParameterSource
 from click.testing import CliRunner
 from requests.exceptions import ConnectionError as RequestsConnectionError, ConnectTimeout
 
@@ -389,7 +389,7 @@ def run_test_check_json_rpc_parity():
         "Parity//v2.5.0-stable-19535333c-20171013/x86_64-linux-gnu/rustc1.20.0"
     )
     assert client is EthClient.PARITY
-    assert {g1, g2, g3, g8} == {VersionSupport.SUPPORTED}
+    assert {g1, g2, g3, g8} == {VersionSupport.UNSUPPORTED}
     assert {g4, g5, g6} == {VersionSupport.WARN}
     assert v1 == "1.7.6"
     assert v2 == "1.7.7"
diff --git a/raiden/tests/unit/test_matrix_transport.py b/raiden/tests/unit/test_matrix_transport.py
index baeb4ee03c..74bb9144ed 100644
--- a/raiden/tests/unit/test_matrix_transport.py
+++ b/raiden/tests/unit/test_matrix_transport.py
@@ -29,13 +29,13 @@
     validate_userid_signature,
 )
 from raiden.tests.utils.factories import make_secret, make_signature, make_signer
-from raiden.tests.utils.transport import ignore_member_join, ignore_messages
+from raiden.tests.utils.transport import ignore_messages
 from raiden.utils.cli import get_matrix_servers
 from raiden.utils.signer import recover
 from raiden.utils.typing import MessageID
 
 
-def test_login_for_the_first_time_must_set_the_display_name():
+def test_login_for_the_first_time_must_set_display_name_and_avatar_url():
     ownserver = "https://ownserver.com"
     api = Mock()
     api.base_url = ownserver
@@ -59,7 +59,12 @@ def mock_login(user, pw, sync=True, device_id=None):  # pylint: disable=unused-a
 
     signer = make_signer()
 
-    user = login(client=client, signer=signer, device_id=DeviceIDs.RAIDEN)
+    user = login(
+        client=client, signer=signer, device_id=DeviceIDs.RAIDEN, capabilities={"test": 1}
+    )
+
+    # avatar_url must have been set
+    user.set_avatar_url.assert_called_once_with("mxc://raiden.network/cap?test=1")
 
     # client.user_id will be set by login
     assert client.user_id.startswith(f"@{to_normalized_address(signer.address)}")
@@ -183,13 +188,11 @@ def response(_):
 def test_make_client(monkeypatch):
     # invalid server url (ftp not supported)
     with pytest.raises(TransportError):
-        make_client(
-            ignore_messages, ignore_member_join, ["ftp://server1.com", "http://server2.com"]
-        )
+        make_client(ignore_messages, ["ftp://server1.com", "http://server2.com"])
 
     # no valid server url
     with pytest.raises(TransportError):
-        make_client(ignore_messages, ignore_member_join, [])
+        make_client(ignore_messages, [])
 
     # valid but unreachable servers
     with pytest.raises(TransportError), monkeypatch.context() as m:
@@ -206,7 +209,6 @@ def test_make_client(monkeypatch):
 
         make_client(
             ignore_messages,
-            ignore_member_join,
             [f"http://server{i}.xyz" for i in range(3)],
         )
 
@@ -217,7 +219,7 @@ def test_make_client(monkeypatch):
         m.setattr(raiden.network.transport.matrix.client.GMatrixHttpApi, "_send", mock_send)
 
         url = "https://server1.xyz"
-        client = make_client(ignore_messages, ignore_member_join, [url])
+        client = make_client(ignore_messages, [url])
         assert isinstance(client, raiden.network.transport.matrix.client.GMatrixClient)
         assert client.api.base_url == url
 
diff --git a/raiden/tests/unit/test_pfs_integration.py b/raiden/tests/unit/test_pfs_integration.py
index 3081644d96..7ee4802e6d 100644
--- a/raiden/tests/unit/test_pfs_integration.py
+++ b/raiden/tests/unit/test_pfs_integration.py
@@ -9,6 +9,7 @@
 import requests
 from eth_utils import encode_hex, is_checksum_address, is_hex, is_hex_address
 
+from raiden.api.v1.encoding import CapabilitiesSchema
 from raiden.constants import RoutingMode
 from raiden.exceptions import ServiceRequestFailed, ServiceRequestIOURejected
 from raiden.network import pathfinding
@@ -40,6 +41,7 @@
 from raiden.utils.capabilities import capconfig_to_dict
 from raiden.utils.formatting import to_checksum_address
 from raiden.utils.keys import privatekey_to_address
+from raiden.utils.signer import Signer
 from raiden.utils.typing import (
     Address,
     AddressMetadata,
@@ -49,10 +51,8 @@
     ChainID,
     Dict,
     PaymentAmount,
-    PeerCapabilities,
     TokenAmount,
     TokenNetworkAddress,
-    UserID,
 )
 
 DEFAULT_FEEDBACK_TOKEN = UUID("381e4a005a4d4687ac200fa1acd15c6f")
@@ -63,10 +63,15 @@ def assert_checksum_address_in_url(url):
     assert any(is_checksum_address(token) for token in url.split("/")), message
 
 
-def make_address_metadata(address) -> AddressMetadata:
+def make_address_metadata(signer: Signer) -> AddressMetadata:
+    user_id = make_user_id(signer.address, "homeserver")
+    signature_bytes = signer.sign(str(user_id).encode())
+    signature_hex = encode_hex(signature_bytes)
+
     return dict(
-        user_id=UserID(make_user_id(address, "homeserver")),
-        capabilities=PeerCapabilities(capconfig_to_dict(CapabilitiesConfig())),
+        user_id=user_id,
+        capabilities=CapabilitiesSchema().dump(capconfig_to_dict(CapabilitiesConfig())),
+        displayname=signature_hex,
     )
 
 
@@ -237,7 +242,7 @@ def happy_path_fixture(chain_state, token_network_state, our_address):
     return addresses, chain_state, channel_states, response, token_network_state
 
 
-def test_routing_mocked_pfs_happy_path(happy_path_fixture, one_to_n_address, our_address):
+def test_routing_mocked_pfs_happy_path(happy_path_fixture, one_to_n_address, our_signer):
     addresses, chain_state, _, response, token_network_state = happy_path_fixture
     _, address2, _, address4 = addresses
 
@@ -246,10 +251,10 @@ def test_routing_mocked_pfs_happy_path(happy_path_fixture, one_to_n_address, our
             chain_state=chain_state,
             token_network_state=token_network_state,
             one_to_n_address=one_to_n_address,
-            from_address=our_address,
+            from_address=our_signer.address,
             to_address=address4,
             amount=50,
-            our_address_metadata=make_address_metadata(our_address),
+            our_address_metadata=make_address_metadata(our_signer),
         )
 
     assert_checksum_address_in_url(patched.call_args[0][0])
@@ -268,7 +273,7 @@ def test_routing_mocked_pfs_happy_path(happy_path_fixture, one_to_n_address, our
 
 
 def test_routing_mocked_pfs_happy_path_with_updated_iou(
-    happy_path_fixture, one_to_n_address, our_address
+    happy_path_fixture, one_to_n_address, our_signer
 ):
     addresses, chain_state, _, response, token_network_state = happy_path_fixture
     _, address2, _, address4 = addresses
@@ -289,10 +294,10 @@ def test_routing_mocked_pfs_happy_path_with_updated_iou(
             chain_state=chain_state,
             token_network_state=token_network_state,
             one_to_n_address=one_to_n_address,
-            from_address=our_address,
+            from_address=our_signer.address,
             to_address=address4,
             amount=50,
-            our_address_metadata=make_address_metadata(our_address),
+            our_address_metadata=make_address_metadata(our_signer),
             iou_json_data=dict(last_iou=last_iou.as_json()),
         )
 
@@ -313,10 +318,10 @@ def test_routing_mocked_pfs_happy_path_with_updated_iou(
 
 
 def test_routing_mocked_pfs_request_error(
-    chain_state, token_network_state, one_to_n_address, our_address
+    chain_state, token_network_state, one_to_n_address, our_signer
 ):
     token_network_state, addresses, _ = create_square_network_topology(
-        token_network_state=token_network_state, our_address=our_address
+        token_network_state=token_network_state, our_address=our_signer.address
     )
     address1, address2, address3, address4 = addresses
 
@@ -332,10 +337,10 @@ def test_routing_mocked_pfs_request_error(
             chain_state=chain_state,
             token_network_state=token_network_state,
             one_to_n_address=one_to_n_address,
-            from_address=our_address,
+            from_address=our_signer.address,
             to_address=address4,
             amount=50,
-            our_address_metadata=make_address_metadata(our_address),
+            our_address_metadata=make_address_metadata(our_signer),
         )
         # Request to PFS failed, but we do not fall back to internal routing
         assert len(routes) == 0
@@ -343,10 +348,10 @@ def test_routing_mocked_pfs_request_error(
 
 
 def test_routing_mocked_pfs_bad_http_code(
-    chain_state, token_network_state, one_to_n_address, our_address
+    chain_state, token_network_state, one_to_n_address, our_signer
 ):
     token_network_state, addresses, _ = create_square_network_topology(
-        token_network_state=token_network_state, our_address=our_address
+        token_network_state=token_network_state, our_address=our_signer.address
     )
     address1, address2, address3, address4 = addresses
 
@@ -362,7 +367,7 @@ def test_routing_mocked_pfs_bad_http_code(
         "result": [
             {
                 "path": [
-                    to_checksum_address(our_address),
+                    to_checksum_address(our_signer.address),
                     to_checksum_address(address2),
                     to_checksum_address(address3),
                     to_checksum_address(address4),
@@ -379,10 +384,10 @@ def test_routing_mocked_pfs_bad_http_code(
             chain_state=chain_state,
             token_network_state=token_network_state,
             one_to_n_address=one_to_n_address,
-            from_address=our_address,
+            from_address=our_signer.address,
             to_address=address4,
             amount=50,
-            our_address_metadata=make_address_metadata(our_address),
+            our_address_metadata=make_address_metadata(our_signer),
         )
         # Request to PFS failed, but we do not fall back to internal routing
         assert len(routes) == 0
@@ -390,10 +395,10 @@ def test_routing_mocked_pfs_bad_http_code(
 
 
 def test_routing_mocked_pfs_invalid_json(
-    chain_state, token_network_state, one_to_n_address, our_address
+    chain_state, token_network_state, one_to_n_address, our_signer
 ):
     token_network_state, addresses, _ = create_square_network_topology(
-        token_network_state=token_network_state, our_address=our_address
+        token_network_state=token_network_state, our_address=our_signer.address
     )
     address1, address2, address3, address4 = addresses
 
@@ -411,10 +416,10 @@ def test_routing_mocked_pfs_invalid_json(
             chain_state=chain_state,
             token_network_state=token_network_state,
             one_to_n_address=one_to_n_address,
-            from_address=our_address,
+            from_address=our_signer.address,
             to_address=address4,
             amount=50,
-            our_address_metadata=make_address_metadata(our_address),
+            our_address_metadata=make_address_metadata(our_signer),
         )
         # Request to PFS failed, but we do not fall back to internal routing
         assert len(routes) == 0
@@ -422,10 +427,10 @@ def test_routing_mocked_pfs_invalid_json(
 
 
 def test_routing_mocked_pfs_invalid_json_structure(
-    chain_state, one_to_n_address, token_network_state, our_address
+    chain_state, one_to_n_address, token_network_state, our_signer
 ):
     token_network_state, addresses, _ = create_square_network_topology(
-        token_network_state=token_network_state, our_address=our_address
+        token_network_state=token_network_state, our_address=our_signer.address
     )
     address1, address2, address3, address4 = addresses
 
@@ -443,10 +448,10 @@ def test_routing_mocked_pfs_invalid_json_structure(
             chain_state=chain_state,
             token_network_state=token_network_state,
             one_to_n_address=one_to_n_address,
-            from_address=our_address,
+            from_address=our_signer.address,
             to_address=address4,
             amount=50,
-            our_address_metadata=make_address_metadata(our_address),
+            our_address_metadata=make_address_metadata(our_signer),
         )
         # Request to PFS failed, but we do not fall back to internal routing
         assert len(routes) == 0
@@ -454,8 +459,9 @@ def test_routing_mocked_pfs_invalid_json_structure(
 
 
 def test_routing_mocked_pfs_unavailable_peer(
-    chain_state, token_network_state, one_to_n_address, our_address
+    chain_state, token_network_state, one_to_n_address, our_signer
 ):
+    our_address = our_signer.address
     token_network_state, addresses, _ = create_square_network_topology(
         token_network_state=token_network_state, our_address=our_address
     )
@@ -493,7 +499,7 @@ def test_routing_mocked_pfs_unavailable_peer(
             from_address=our_address,
             to_address=address4,
             amount=50,
-            our_address_metadata=make_address_metadata(our_address),
+            our_address_metadata=make_address_metadata(our_signer),
         )
         # Node with address2 is not reachable, so even if the only route sent by the PFS
         # is over address2, the internal routing does not provide
@@ -681,7 +687,7 @@ def assert_failed_pfs_request(
                 assert post_paths.call_count == expected_requests
 
 
-def test_routing_in_direct_channel(happy_path_fixture, our_address, one_to_n_address):
+def test_routing_in_direct_channel(happy_path_fixture, our_signer, one_to_n_address):
     addresses, chain_state, _, _, token_network_state = happy_path_fixture
     address1, _, _, _ = addresses
 
@@ -691,17 +697,18 @@ def test_routing_in_direct_channel(happy_path_fixture, our_address, one_to_n_add
         "raiden.routing.query_address_metadata"
     ) as pfs_user_request:
         pfs_route_request.return_value = None, [], "feedback_token"
+        pfs_user_request.return_value = None
         _, routes, _ = get_best_routes(
             chain_state=chain_state,
             token_network_address=token_network_state.address,
             one_to_n_address=one_to_n_address,
-            from_address=our_address,
+            from_address=our_signer.address,
             to_address=address1,
             amount=PaymentAmount(50),
             previous_address=None,
             pfs_config=PFS_CONFIG,
             privkey=PRIVKEY,
-            our_address_metadata=make_address_metadata(our_address),
+            our_address_metadata=make_address_metadata(our_signer),
         )
         assert routes[0].next_hop_address == address1
         assert not pfs_route_request.called
@@ -715,13 +722,13 @@ def test_routing_in_direct_channel(happy_path_fixture, our_address, one_to_n_add
             chain_state=chain_state,
             token_network_address=token_network_state.address,
             one_to_n_address=one_to_n_address,
-            from_address=our_address,
+            from_address=our_signer.address,
             to_address=address1,
             amount=PaymentAmount(51),
             previous_address=None,
             pfs_config=PFS_CONFIG,
             privkey=PRIVKEY,
-            our_address_metadata=make_address_metadata(our_address),
+            our_address_metadata=make_address_metadata(our_signer),
         )
 
         assert pfs_request.called
@@ -752,7 +759,7 @@ def valid_response_json():
 
 
 def test_query_paths_with_second_try(query_paths_args, valid_response_json):
-    """ IOU rejection errors that are expected to result in an unaltered second attempt """
+    """IOU rejection errors that are expected to result in an unaltered second attempt"""
     for try_again in (PFSError.BAD_IOU, PFSError.MISSING_IOU, PFSError.USE_THIS_IOU):
         response = [dict(error_code=try_again.value)] * 2
         assert_failed_pfs_request(
@@ -767,7 +774,7 @@ def test_query_paths_with_second_try(query_paths_args, valid_response_json):
 
 
 def test_query_paths_with_scrapped_iou(query_paths_args, valid_response_json):
-    """ Errors that will result in reattempting with a new iou """
+    """Errors that will result in reattempting with a new iou"""
     for scrap_iou in (PFSError.IOU_ALREADY_CLAIMED, PFSError.IOU_EXPIRED_TOO_EARLY):
         response = [dict(error_code=scrap_iou.value)] * 2
         assert_failed_pfs_request(
@@ -789,7 +796,7 @@ def test_query_paths_with_scrapped_iou(query_paths_args, valid_response_json):
 
 
 def test_query_paths_with_unrecoverable_pfs_error(query_paths_args):
-    " No retries after unrecoverable errors. "
+    "No retries after unrecoverable errors."
     for unrecoverable in (
         PFSError.INVALID_REQUEST,
         PFSError.INVALID_SIGNATURE,
@@ -809,7 +816,7 @@ def test_query_paths_with_unrecoverable_pfs_error(query_paths_args):
 
 
 def test_insufficient_payment(query_paths_args, valid_response_json):
-    """ When the PFS complains about insufficient fees, the client must update it's fee info """
+    """When the PFS complains about insufficient fees, the client must update it's fee info"""
     insufficient_response = dict(error_code=PFSError.INSUFFICIENT_SERVICE_PAYMENT.value)
 
     # PFS fails to return info
@@ -846,7 +853,7 @@ def test_insufficient_payment(query_paths_args, valid_response_json):
 
 
 def test_query_paths_with_multiple_errors(query_paths_args):
-    " Max. number of attempts is not exceeded also if there is a new recoverable issue. "
+    "Max. number of attempts is not exceeded also if there is a new recoverable issue."
     different_recoverable_errors = [
         dict(error_code=PFSError.BAD_IOU.value),
         dict(error_code=PFSError.IOU_ALREADY_CLAIMED.value),
@@ -857,7 +864,7 @@ def test_query_paths_with_multiple_errors(query_paths_args):
 
 
 def test_post_pfs_feedback(query_paths_args):
-    """ Test POST feedback to PFS """
+    """Test POST feedback to PFS"""
 
     feedback_token = uuid4()
     token_network_address = factories.make_token_network_address()
@@ -913,7 +920,7 @@ def test_post_pfs_feedback(query_paths_args):
 
 
 def test_no_iou_when_pfs_price_0(query_paths_args):
-    """ Test that no IOU is sent when PFS is for free """
+    """Test that no IOU is sent when PFS is for free"""
     query_paths_args["pfs_config"] = PFSConfig(
         info=PFSInfo(
             url="abc",
@@ -964,7 +971,7 @@ def test_no_iou_when_pfs_price_0(query_paths_args):
 
 
 def test_two_parallel_queries(query_paths_args):
-    """ Test that only one IOU is being processed at a time. """
+    """Test that only one IOU is being processed at a time."""
 
     # We mock one query to last at least 0.2s
     def mocked_json_response_with_sleep(**kwargs):  # pylint: disable=unused-argument
diff --git a/raiden/tests/unit/test_serialization.py b/raiden/tests/unit/test_serialization.py
index 201fa7edb4..6fb7e5d600 100644
--- a/raiden/tests/unit/test_serialization.py
+++ b/raiden/tests/unit/test_serialization.py
@@ -276,3 +276,38 @@ def test_hashing():
     """All messages must be hashable for de-duplication to work."""
     for message in messages:
         assert hash(message), "hashing failed"
+
+
+def test_deserialize_without_metadata():
+    """address_metadata is optional in LockedTransfer messages
+
+    Regression test for https://github.com/raiden-network/raiden/issues/7052
+    """
+    m = {
+        "type": "LockedTransfer",
+        "message_identifier": "1621260201713",
+        "chain_id": "4321",
+        "token_network_address": "0x32626F60da0aF910293EcdeC2123932db4138A81",
+        "channel_identifier": "2",
+        "nonce": "1",
+        "transferred_amount": "0",
+        "locked_amount": "100",
+        "locksroot": "0x70f5bc325544293c71c6dbd0b47ce3acc0c8dc1dc2459c2606d9e6a264ff8a42",
+        "payment_identifier": "1621260201680",
+        "token": "0x0c7309dF25335dDf05ca0853Fc95Df49421DaF02",
+        "recipient": "0x517aAD51D0e9BbeF3c64803F86b3B9136641D9ec",
+        "lock": {
+            "amount": "100",
+            "expiration": "335",
+            "secrethash": "0xec19a77fb42be8b7c4ca467f83aa577007da49bd18fe80c7abb8ca10d9b02d46",
+        },
+        "target": "0x517aAD51D0e9BbeF3c64803F86b3B9136641D9ec",
+        "initiator": "0x14791697260E4c9A71f18484C9f997B308e59325",
+        "metadata": {"routes": [{"route": ["0x517aAD51D0e9BbeF3c64803F86b3B9136641D9ec"]}]},
+        "signature": (
+            "0x536ab67c1a7d5243d92edb1f59e3bb64335526a31622110bf9d80f16c26abd464c4"
+            "9b0cf736dc48481aa1b54f1d340acca93caf4540cc3333e725db507f4769d1b"
+        ),
+    }
+
+    MessageSerializer.deserialize(json.dumps(m))
diff --git a/raiden/tests/unit/test_sqlite.py b/raiden/tests/unit/test_sqlite.py
index 7918ac8550..be41746f4b 100644
--- a/raiden/tests/unit/test_sqlite.py
+++ b/raiden/tests/unit/test_sqlite.py
@@ -26,7 +26,6 @@
 from raiden.transfer.mediated_transfer.events import (
     SendLockedTransfer,
     SendLockExpired,
-    SendRefundTransfer,
     SendUnlock,
 )
 from raiden.transfer.mediated_transfer.state_change import (
@@ -318,19 +317,10 @@ def test_get_event_with_balance_proof():
         canonical_identifier=factories.make_canonical_identifier(),
     )
 
-    refund_transfer = SendRefundTransfer(
-        recipient=partner_address,
-        recipient_metadata=None,
-        message_identifier=MessageID(next(counter)),
-        transfer=make_transfer_from_counter(counter),
-        canonical_identifier=factories.make_canonical_identifier(),
-    )
-
     events_balanceproofs = [
         (lock_expired, lock_expired.balance_proof),
         (locked_transfer, locked_transfer.balance_proof),
         (send_balance_proof, send_balance_proof.balance_proof),
-        (refund_transfer, refund_transfer.transfer.balance_proof),
     ]
 
     state_change = Block(BlockNumber(1), BlockGasLimit(1), factories.make_block_hash())
diff --git a/raiden/tests/unit/test_startup.py b/raiden/tests/unit/test_startup.py
index 156fca19f6..b2b9310d3b 100644
--- a/raiden/tests/unit/test_startup.py
+++ b/raiden/tests/unit/test_startup.py
@@ -1,5 +1,5 @@
 from typing import Any, Dict
-from unittest.mock import patch
+from unittest.mock import MagicMock, patch
 
 import pytest
 from eth_utils import to_canonical_address
@@ -9,6 +9,7 @@
 from raiden.network import pathfinding
 from raiden.network.pathfinding import PFSInfo
 from raiden.settings import RaidenConfig, ServiceConfig
+from raiden.tasks import check_chain_id
 from raiden.tests.utils.factories import make_address
 from raiden.tests.utils.mocks import MockProxyManager, MockWeb3
 from raiden.ui.checks import check_ethereum_chain_id
@@ -64,6 +65,13 @@ def test_check_chain_id_raises_with_mismatching_ids():
         check_ethereum_chain_id(ChainID(61), MockWeb3(68))
 
 
+def test_check_chain_id_raises_with_connection_error():
+    mock_web3 = MockWeb3(61)
+    mock_web3.eth.chain_id = MagicMock(side_effect=ConnectionError)
+    with pytest.raises(RuntimeError):
+        check_chain_id(ChainID(61), mock_web3)
+
+
 @pytest.mark.parametrize("netid", [1, 3, 4, 5, 627])
 def test_setup_does_not_raise_with_matching_ids(netid):
     """Test that network setup works for the known network ids"""
diff --git a/raiden/tests/unit/test_utils.py b/raiden/tests/unit/test_utils.py
index 1db725119c..38ed1a109d 100644
--- a/raiden/tests/unit/test_utils.py
+++ b/raiden/tests/unit/test_utils.py
@@ -64,7 +64,7 @@ def test_recover_exception(signature, nested_exception):
 
 
 def test_get_http_rtt_happy(requests_responses):
-    """ Ensure get_http_rtt returns the average RTT over the number of samples. """
+    """Ensure get_http_rtt returns the average RTT over the number of samples."""
     delay = iter([0.05, 0.05, 0.2])
 
     def response(_):
@@ -78,7 +78,7 @@ def response(_):
 
 
 def test_get_http_rtt_ignore_failing(requests_responses):
-    """ Ensure get_http_rtt ignores failing servers. """
+    """Ensure get_http_rtt ignores failing servers."""
 
     # RequestException (e.g. DNS not resolvable, server not reachable)
     requests_responses.add(responses.GET, "http://url1", body=requests.RequestException())
diff --git a/raiden/tests/unit/transfer/mediated_transfer/test_events.py b/raiden/tests/unit/transfer/mediated_transfer/test_events.py
deleted file mode 100644
index 0574056c05..0000000000
--- a/raiden/tests/unit/transfer/mediated_transfer/test_events.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from raiden.storage.serialization import JSONSerializer
-from raiden.tests.utils import factories
-from raiden.transfer.mediated_transfer.events import SendRefundTransfer
-
-
-def test_send_refund_transfer_contains_balance_proof():
-    recipient = factories.make_address()
-    transfer = factories.create(factories.LockedTransferUnsignedStateProperties())
-    message_identifier = 1
-    event = SendRefundTransfer(
-        recipient=recipient,
-        recipient_metadata=None,
-        message_identifier=message_identifier,
-        transfer=transfer,
-        canonical_identifier=factories.make_canonical_identifier(),
-    )
-
-    assert hasattr(event, "balance_proof")
-    assert JSONSerializer.deserialize(JSONSerializer.serialize(event)) == event
diff --git a/raiden/tests/unit/transfer/mediated_transfer/test_initiatorstate.py b/raiden/tests/unit/transfer/mediated_transfer/test_initiatorstate.py
index 5e2985fa11..543e2f92fc 100644
--- a/raiden/tests/unit/transfer/mediated_transfer/test_initiatorstate.py
+++ b/raiden/tests/unit/transfer/mediated_transfer/test_initiatorstate.py
@@ -835,7 +835,7 @@ def test_cancel_transfer():
 
 
 def test_cancelpayment():
-    """ A payment can be cancelled as long as the secret has not been revealed. """
+    """A payment can be cancelled as long as the secret has not been revealed."""
     setup = setup_initiator_tests(amount=2 * MAXIMUM_PENDING_TRANSFERS * UNIT_TRANSFER_AMOUNT)
     assert isinstance(setup.current_state, InitiatorPaymentState)
 
diff --git a/raiden/tests/unit/transfer/mediated_transfer/test_mediation_fee.py b/raiden/tests/unit/transfer/mediated_transfer/test_mediation_fee.py
index ab98066e1b..a1294ee5c9 100644
--- a/raiden/tests/unit/transfer/mediated_transfer/test_mediation_fee.py
+++ b/raiden/tests/unit/transfer/mediated_transfer/test_mediation_fee.py
@@ -272,7 +272,7 @@ def test_rebalancing_fee_calculation():
     ],
 )
 def test_get_lock_amount_after_fees(flat_fee, prop_fee, initial_amount, expected_amount):
-    """ Tests mediation fee deduction. """
+    """Tests mediation fee deduction."""
     prop_fee_per_channel = ppm_fee_per_channel(ProportionalFeeAmount(prop_fee))
     lock = make_hash_time_lock_state(amount=initial_amount)
     channel_in = factories.create(
@@ -315,7 +315,7 @@ def test_get_lock_amount_after_fees(flat_fee, prop_fee, initial_amount, expected
 def test_get_lock_amount_after_fees_imbalanced_channel(
     cap_fees, flat_fee, prop_fee, imbalance_fee, initial_amount, expected_amount
 ):
-    """ Tests mediation fee deduction. """
+    """Tests mediation fee deduction."""
     balance = TokenAmount(100_000)
     prop_fee_per_channel = ppm_fee_per_channel(ProportionalFeeAmount(prop_fee))
     imbalance_fee = calculate_imbalance_fees(
@@ -443,7 +443,7 @@ def test_fee_round_trip(flat_fee, prop_fee, imbalance_fee, amount, balance1, bal
 )
 @settings(suppress_health_check=[HealthCheck.filter_too_much])
 def test_fee_add_remove_invariant(flat_fee, prop_fee, imbalance_fee, amount, balance1, balance2):
-    """ First adding and then removing fees must yield the original value """
+    """First adding and then removing fees must yield the original value"""
     total_balance = TokenAmount(100_000_000_000_000_000_000)
     prop_fee_per_channel = ppm_fee_per_channel(ProportionalFeeAmount(prop_fee))
     imbalance_fee = calculate_imbalance_fees(
@@ -519,7 +519,7 @@ def make_channel_pair(
 
 
 def test_mfee1():
-    """ Unit test for the fee calculation in the mfee1_flat_fee scenario """
+    """Unit test for the fee calculation in the mfee1_flat_fee scenario"""
     amount = 10_000
     deposit = 100_000
     flat_fee = 100 // 2
@@ -543,7 +543,7 @@ def test_mfee1():
 
 
 def test_mfee2():
-    """ Unit test for the fee calculation in the mfee2_proportional_fees scenario """
+    """Unit test for the fee calculation in the mfee2_proportional_fees scenario"""
     amount = 10_000
     deposit = 100_000
     prop_fee = ppm_fee_per_channel(ProportionalFeeAmount(10_000))
@@ -567,7 +567,7 @@ def test_mfee2():
 
 
 def test_mfee3():
-    """ Unit test for the fee calculation in the mfee3_only_imbalance_fees scenario """
+    """Unit test for the fee calculation in the mfee3_only_imbalance_fees scenario"""
     amount = 500_000_000_000_000_000
     deposit = TokenAmount(1_000_000_000_000_000_000)
     imbalance_penalty = calculate_imbalance_fees(deposit, ProportionalFeeAmount(10_000))
@@ -595,7 +595,7 @@ def test_mfee3():
 
 
 def test_mfee4():
-    """ Unit test for the fee calculation in the mfee4_combined_fees scenario """
+    """Unit test for the fee calculation in the mfee4_combined_fees scenario"""
     amount = PaymentAmount(500_000_000_000_000_000)
     deposit = 1_000_000_000_000_000_000
     prop_fee = ppm_fee_per_channel(ProportionalFeeAmount(10_000))
diff --git a/raiden/tests/unit/transfer/mediated_transfer/test_mediatorstate.py b/raiden/tests/unit/transfer/mediated_transfer/test_mediatorstate.py
index 50b5fd10a3..489119e3d2 100644
--- a/raiden/tests/unit/transfer/mediated_transfer/test_mediatorstate.py
+++ b/raiden/tests/unit/transfer/mediated_transfer/test_mediatorstate.py
@@ -57,7 +57,6 @@
     EventUnlockSuccess,
     SendLockedTransfer,
     SendLockExpired,
-    SendRefundTransfer,
     SendSecretReveal,
     SendUnlock,
 )
@@ -76,7 +75,6 @@
     ChannelState,
     HashTimeLockState,
     HopState,
-    NettingChannelState,
     RouteState,
     message_identifier_from_prng,
 )
@@ -97,7 +95,6 @@
     BlockNumber,
     BlockTimeout,
     FeeAmount,
-    InitiatorAddress,
     MessageID,
     Nonce,
     PaymentID,
@@ -115,7 +112,7 @@ def make_route_from_channelstate(channel_state):
 
 
 def test_is_lock_valid():
-    """ A hash time lock is valid up to the expiration block. """
+    """A hash time lock is valid up to the expiration block."""
     expiration = BlockExpiration(10)
     assert mediator.is_lock_valid(expiration, BlockNumber(5)) is True
     assert (
@@ -166,7 +163,7 @@ def test_is_safe_to_wait():
 
 
 def test_is_channel_usable_for_mediation():
-    """ Check rules that determine if a channel can be used for transfers """
+    """Check rules that determine if a channel can be used for transfers"""
     reveal_timeout = BlockTimeout(30)
     timeout_blocks = BlockTimeout(reveal_timeout + 10)
     amount = UNIT_TRANSFER_AMOUNT
@@ -305,7 +302,7 @@ def test_set_payee():
 
 
 def test_events_for_expired_pairs():
-    """ The transfer pair must switch to expired at the right block. """
+    """The transfer pair must switch to expired at the right block."""
     setup = factories.make_transfers_pair(2)
     pair = setup.transfers_pair[0]
 
@@ -333,50 +330,6 @@ def test_events_for_expired_pairs():
     assert pair.payer_state == "payer_expired"
 
 
-def test_events_for_refund():
-    amount = TokenAmount(10)
-    block_number = BlockNumber(1)
-    pseudo_random_generator = random.Random()
-
-    our_state = factories.NettingChannelEndStateProperties(balance=amount)
-    partner_state = replace(our_state, address=UNIT_TRANSFER_SENDER)
-    refund_channel = factories.create(
-        factories.NettingChannelStateProperties(our_state=our_state, partner_state=partner_state)
-    )
-
-    transfer_data = LockedTransferSignedStateProperties(
-        amount=amount,
-        expiration=BlockExpiration(30),
-        canonical_identifier=refund_channel.canonical_identifier,
-    )
-    received_transfer = create(transfer_data)
-
-    is_valid, _, msg = channel.handle_receive_lockedtransfer(refund_channel, received_transfer)
-    assert is_valid, msg
-
-    transfer_pair, refund_events = mediator.backward_transfer_pair(
-        refund_channel, received_transfer, pseudo_random_generator, block_number
-    )
-
-    assert search_for_item(
-        refund_events,
-        SendRefundTransfer,
-        {
-            "transfer": {
-                "lock": {
-                    "expiration": received_transfer.lock.expiration,
-                    "amount": amount,
-                    "secrethash": received_transfer.lock.secrethash,
-                }
-            },
-            # pylint: disable=E1101
-            "recipient": refund_channel.partner_state.address,
-        },
-    )
-    assert transfer_pair
-    assert transfer_pair.payer_transfer == received_transfer
-
-
 def test_events_for_secretreveal():
     """The secret is revealed backwards to the payer once the payee sent the
     SecretReveal.
@@ -431,7 +384,7 @@ def test_events_for_secretreveal():
 
 
 def test_events_for_secretreveal_secret_unknown():
-    """ When the secret is not known there is nothing to do. """
+    """When the secret is not known there is nothing to do."""
     pseudo_random_generator = random.Random()
 
     setup = factories.make_transfers_pair(3)
@@ -593,7 +546,7 @@ def test_events_for_balanceproof_middle_secret():
 
 
 def test_events_for_balanceproof_secret_unknown():
-    """ Nothing to do if the secret is not known. """
+    """Nothing to do if the secret is not known."""
     pseudo_random_generator = random.Random()
     setup = factories.make_transfers_pair(3, block_number=1)
 
@@ -610,7 +563,7 @@ def test_events_for_balanceproof_secret_unknown():
 
 
 def test_events_for_balanceproof_lock_expired():
-    """ The balance proof should not be sent if the lock has expired. """
+    """The balance proof should not be sent if the lock has expired."""
     pseudo_random_generator = random.Random()
     setup = factories.make_transfers_pair(4, block_number=1)
     last_pair = setup.transfers_pair[-1]
@@ -683,7 +636,7 @@ def test_events_for_onchain_secretreveal():
 
 
 def test_events_for_onchain_secretreveal_once():
-    """ Secret must be registered on-chain only once. """
+    """Secret must be registered on-chain only once."""
     setup = factories.make_transfers_pair(4, block_number=1)
     pair = setup.transfers_pair[0]
     channel_state = mediator.get_payer_channel(setup.channel_map, pair)
@@ -1083,7 +1036,6 @@ def test_no_valid_routes():
         "either to handle future available routes, or lock expired messages"
     )
     assert iteration.new_state is not None, msg
-    assert search_for_item(iteration.events, SendRefundTransfer, {})
 
 
 def test_lock_timeout_larger_than_settlement_period_must_be_ignored():
@@ -2176,129 +2128,6 @@ def test_imbalance_penalty_prevents_transfer():
     assert not pair
 
 
-def test_backward_transfer_pair_with_fees_deducted():
-    amount = 10
-    fee = FeeAmount(0)  # Fee handling for refunds is currently undefined, so set it to 0 for now.
-
-    end_state = factories.NettingChannelEndStateProperties(balance=TokenAmount(amount + fee))
-    partner_state = replace(end_state, address=UNIT_TRANSFER_SENDER)
-    refund_channel: NettingChannelState = factories.create(
-        factories.NettingChannelStateProperties(our_state=end_state, partner_state=partner_state)
-    )
-
-    refund_channel.fee_schedule.flat = fee
-
-    transfer_data = LockedTransferSignedStateProperties(
-        amount=TokenAmount(amount + fee),
-        expiration=BlockExpiration(10),
-        canonical_identifier=refund_channel.canonical_identifier,
-        locked_amount=TokenAmount(amount + fee),
-    )
-    received_transfer = create(transfer_data)
-
-    is_valid, _, msg = channel.handle_receive_lockedtransfer(refund_channel, received_transfer)
-    assert is_valid, msg
-
-    transfer_pair, refund_events = mediator.backward_transfer_pair(
-        backward_channel=refund_channel,
-        payer_transfer=received_transfer,
-        pseudo_random_generator=random.Random(),
-        block_number=BlockNumber(1),
-    )
-
-    assert search_for_item(
-        refund_events,
-        SendRefundTransfer,
-        {
-            "transfer": {
-                "lock": {
-                    "expiration": received_transfer.lock.expiration,
-                    "amount": amount,
-                    "secrethash": received_transfer.lock.secrethash,
-                }
-            },
-            # pylint: disable=E1101
-            "recipient": refund_channel.partner_state.address,
-        },
-    )
-    assert transfer_pair
-    assert transfer_pair.payer_transfer == received_transfer
-
-
-def test_sanity_check_for_refund_transfer_with_fees():
-    fee = FeeAmount(0)  # Fee handling for refunds is currently undefined, so set it to 0 for now.
-    channels = make_channel_set(
-        [
-            NettingChannelStateProperties(
-                fee_schedule=FeeScheduleState(
-                    flat=FeeAmount(fee), proportional=ProportionalFeeAmount(0)
-                ),
-                canonical_identifier=make_canonical_identifier(channel_identifier=1),
-                our_state=NettingChannelEndStateProperties.OUR_STATE,  # type: ignore
-                partner_state=NettingChannelEndStateProperties(
-                    balance=TokenAmount(UNIT_TRANSFER_AMOUNT + fee), address=UNIT_TRANSFER_SENDER
-                ),
-            ),
-            NettingChannelStateProperties(
-                make_canonical_identifier(channel_identifier=2),
-                our_state=replace(
-                    NettingChannelEndStateProperties.OUR_STATE,  # type: ignore
-                    balance=TokenAmount(UNIT_TRANSFER_AMOUNT - 1),
-                ),
-            ),
-            NettingChannelStateProperties(
-                make_canonical_identifier(channel_identifier=3),
-                our_state=replace(
-                    NettingChannelEndStateProperties.OUR_STATE,  # type: ignore
-                    balance=TokenAmount(0),
-                ),
-            ),
-        ]
-    )
-
-    next_hop_address = channels[1].partner_state.address
-
-    from_transfer_amount = TokenAmount(UNIT_TRANSFER_AMOUNT + fee)
-    from_transfer = factories.make_signed_transfer_for(
-        channel_state=channels[0],
-        properties=LockedTransferSignedStateProperties(
-            initiator=InitiatorAddress(UNIT_TRANSFER_SENDER),
-            amount=from_transfer_amount,
-            route_states=factories.create_route_states_from_routes(
-                routes=[
-                    [factories.UNIT_OUR_ADDRESS, next_hop_address, factories.UNIT_TRANSFER_TARGET]
-                ]
-            ),
-        ),
-    )
-
-    assert from_transfer.routes[0][0] == factories.UNIT_OUR_ADDRESS, "not matching"
-    assert from_transfer.routes[0][1] == next_hop_address, "not right next hop"
-
-    iteration = mediator.state_transition(
-        mediator_state=None,
-        state_change=mediator_make_init_action(channels, from_transfer),
-        channelidentifiers_to_channels=channels.channel_map,
-        addresses_to_channel=channels.addresses_to_channel(),
-        pseudo_random_generator=random.Random(),
-        block_number=BlockNumber(1),
-        block_hash=factories.make_block_hash(),
-    )
-    msg = (
-        "The task must be kept alive, "
-        "either to handle future available routes, or lock expired messages"
-    )
-    assert iteration.new_state is not None, msg
-    assert (
-        search_for_item(
-            iteration.events,
-            SendRefundTransfer,
-            {"transfer": {"lock": {"amount": UNIT_TRANSFER_AMOUNT}}},
-        )
-        is not None
-    )
-
-
 def test_receive_unlock():
     channels = mediator_make_channel_pair()
     state = MediatorTransferState(secrethash=factories.UNIT_SECRETHASH, routes=[])
diff --git a/raiden/tests/unit/transfer/mediated_transfer/test_mediatorstate_regression.py b/raiden/tests/unit/transfer/mediated_transfer/test_mediatorstate_regression.py
index c465e5c28b..ca98b88074 100644
--- a/raiden/tests/unit/transfer/mediated_transfer/test_mediatorstate_regression.py
+++ b/raiden/tests/unit/transfer/mediated_transfer/test_mediatorstate_regression.py
@@ -1,8 +1,6 @@
 # pylint: disable=invalid-name,too-many-locals,too-many-arguments,too-many-lines
 import random
 
-from eth_utils import keccak
-
 from raiden.messages.decode import balanceproof_from_envelope
 from raiden.messages.encode import message_from_sendevent
 from raiden.tests.utils import factories
@@ -12,11 +10,7 @@
     HOP2_KEY,
     UNIT_SECRET,
     UNIT_SECRETHASH,
-    UNIT_TOKEN_ADDRESS,
     UNIT_TRANSFER_AMOUNT,
-    UNIT_TRANSFER_IDENTIFIER,
-    UNIT_TRANSFER_INITIATOR,
-    UNIT_TRANSFER_TARGET,
     NettingChannelEndStateProperties,
     NettingChannelStateProperties,
     make_channel_set,
@@ -27,14 +21,12 @@
     EventUnlockClaimFailed,
     SendLockedTransfer,
     SendLockExpired,
-    SendRefundTransfer,
 )
 from raiden.transfer.mediated_transfer.state import MediatorTransferState
 from raiden.transfer.mediated_transfer.state_change import (
     ActionInitMediator,
     ReceiveLockExpired,
     ReceiveSecretReveal,
-    ReceiveTransferRefund,
 )
 from raiden.transfer.state import message_identifier_from_prng
 from raiden.transfer.state_change import Block, ContractReceiveSecretReveal
@@ -125,111 +117,6 @@ def test_payer_enter_danger_zone_with_transfer_payed():
     )
 
 
-def test_regression_send_refund():
-    """Regression test for discarded refund transfer.
-
-    The handle_refundtransfer used to discard events from the channel state
-    machine, which led to the state being updated but the message to the
-    partner was never sent.
-
-    Also, for issue: https://github.com/raiden-network/raiden/issues/3170
-    It was noticed that when receiving the same refund transfer twice, the mediator
-    would detect an invalid refund and clear the mediator state. So the test also
-    checks that mediator rejects the duplicate transfer and keeps the mediator
-    state unchanged.
-    """
-    pseudo_random_generator = random.Random()
-    setup = factories.make_transfers_pair(3)
-
-    mediator_state = MediatorTransferState(secrethash=UNIT_SECRETHASH, routes=[])
-    mediator_state.transfers_pair = setup.transfers_pair
-
-    last_pair = setup.transfers_pair[-1]
-    canonical_identifier = last_pair.payee_transfer.balance_proof.canonical_identifier
-    lock_expiration = last_pair.payee_transfer.lock.expiration
-
-    received_transfer = factories.create(
-        factories.LockedTransferSignedStateProperties(
-            expiration=lock_expiration,
-            payment_identifier=UNIT_TRANSFER_IDENTIFIER,
-            canonical_identifier=canonical_identifier,
-            sender=setup.channels.partner_address(2),
-            pkey=setup.channels.partner_privatekeys[2],
-            message_identifier=factories.make_message_identifier(),
-        )
-    )
-
-    # All three channels have been used
-    refund_state_change = ReceiveTransferRefund(
-        transfer=received_transfer,
-        balance_proof=received_transfer.balance_proof,
-        sender=received_transfer.balance_proof.sender,  # pylint: disable=no-member
-    )
-
-    iteration = mediator.handle_refundtransfer(
-        mediator_state=mediator_state,
-        mediator_state_change=refund_state_change,
-        channelidentifiers_to_channels=setup.channel_map,
-        addresses_to_channel=setup.channels.addresses_to_channel(),
-        pseudo_random_generator=pseudo_random_generator,
-        block_number=setup.block_number,
-    )
-
-    first_pair = setup.transfers_pair[0]
-    first_payer_transfer = first_pair.payer_transfer
-    payer_channel = mediator.get_payer_channel(setup.channel_map, first_pair)
-    lock = channel.get_lock(end_state=payer_channel.partner_state, secrethash=UNIT_SECRETHASH)
-    token_network_address = first_payer_transfer.balance_proof.token_network_address
-    assert search_for_item(
-        iteration.events,
-        SendRefundTransfer,
-        {
-            "recipient": setup.channels.partner_address(0),
-            "queue_identifier": {
-                "recipient": setup.channels.partner_address(0),
-                "canonical_identifier": {
-                    "chain_identifier": first_payer_transfer.balance_proof.chain_id,
-                    "token_network_address": token_network_address,
-                    "channel_identifier": first_payer_transfer.balance_proof.channel_identifier,
-                },
-            },
-            "transfer": {
-                "payment_identifier": UNIT_TRANSFER_IDENTIFIER,
-                "token": UNIT_TOKEN_ADDRESS,
-                "balance_proof": {
-                    "transferred_amount": 0,
-                    "locked_amount": UNIT_TRANSFER_AMOUNT,
-                    "locksroot": keccak(lock.encoded),
-                    "token_network_address": token_network_address,
-                    "channel_identifier": first_payer_transfer.balance_proof.channel_identifier,
-                    "chain_id": first_payer_transfer.balance_proof.chain_id,
-                },
-                "lock": {
-                    "amount": lock.amount,
-                    "expiration": lock.expiration,
-                    "secrethash": lock.secrethash,
-                },
-                "initiator": UNIT_TRANSFER_INITIATOR,
-                "target": UNIT_TRANSFER_TARGET,
-            },
-        },
-    )
-
-    duplicate_iteration = mediator.handle_refundtransfer(
-        mediator_state=iteration.new_state,
-        mediator_state_change=refund_state_change,
-        channelidentifiers_to_channels=setup.channel_map,
-        addresses_to_channel=setup.channels.addresses_to_channel(),
-        pseudo_random_generator=pseudo_random_generator,
-        block_number=setup.block_number,
-    )
-
-    assert search_for_item(duplicate_iteration.events, SendRefundTransfer, {}) is None
-
-    assert duplicate_iteration.new_state is not None
-    assert duplicate_iteration.new_state == iteration.new_state
-
-
 def test_regression_mediator_send_lock_expired_with_new_block():
     """The mediator must send the lock expired, but it must **not** clear
     itself if it has not **received** the corresponding message.
@@ -334,7 +221,6 @@ def test_regression_mediator_task_no_routes():
     assert init_iteration.new_state is not None, msg
     assert init_iteration.new_state.waiting_transfer.transfer == payer_transfer
     assert search_for_item(init_iteration.events, SendLockedTransfer, {}) is None
-    assert search_for_item(init_iteration.events, SendRefundTransfer, {}) is None
 
     secrethash = UNIT_SECRETHASH
     lock = channels[0].partner_state.secrethashes_to_lockedlocks[secrethash]
diff --git a/raiden/tests/unit/transfer/mediated_transfer/test_targetstate.py b/raiden/tests/unit/transfer/mediated_transfer/test_targetstate.py
index 0fbd30b4b4..18ef58937a 100644
--- a/raiden/tests/unit/transfer/mediated_transfer/test_targetstate.py
+++ b/raiden/tests/unit/transfer/mediated_transfer/test_targetstate.py
@@ -176,7 +176,7 @@ def test_events_for_onchain_secretreveal():
 
 
 def test_handle_inittarget():
-    """ Init transfer must send a secret request if the expiration is valid. """
+    """Init transfer must send a secret request if the expiration is valid."""
     block_number = 1
     pseudo_random_generator = random.Random()
 
@@ -216,7 +216,7 @@ def test_handle_inittarget():
 
 
 def test_handle_inittarget_bad_expiration():
-    """ Init transfer must do nothing if the expiration is bad. """
+    """Init transfer must do nothing if the expiration is bad."""
     block_number = 1
     pseudo_random_generator = random.Random()
 
@@ -394,7 +394,7 @@ def test_handle_onchain_secretreveal():
 
 
 def test_handle_block():
-    """ Increase the block number. """
+    """Increase the block number."""
     setup = make_target_state()
 
     new_block = Block(
@@ -416,7 +416,7 @@ def test_handle_block():
 
 
 def test_handle_block_equal_block_number():
-    """ Nothing changes. """
+    """Nothing changes."""
     setup = make_target_state()
 
     new_block = Block(block_number=1, gas_limit=1, block_hash=factories.make_transaction_hash())
@@ -434,7 +434,7 @@ def test_handle_block_equal_block_number():
 
 
 def test_handle_block_lower_block_number():
-    """ Nothing changes. """
+    """Nothing changes."""
     setup = make_target_state(block_number=10)
 
     new_block = Block(
@@ -454,7 +454,7 @@ def test_handle_block_lower_block_number():
 
 
 def test_state_transition():
-    """ Happy case testing. """
+    """Happy case testing."""
     lock_amount = 7
     block_number = 1
     initiator = factories.make_address()
diff --git a/raiden/tests/unit/transfer/test_source_routing.py b/raiden/tests/unit/transfer/test_source_routing.py
index 0c6d0dd122..0134e10598 100644
--- a/raiden/tests/unit/transfer/test_source_routing.py
+++ b/raiden/tests/unit/transfer/test_source_routing.py
@@ -1,16 +1,19 @@
 import random
+from urllib.parse import urlparse
+
+from eth_utils import encode_hex, to_normalized_address
 
 from raiden.messages.metadata import Metadata, RouteMetadata
 from raiden.messages.transfers import LockedTransfer, RefundTransfer
 from raiden.storage.serialization import DictSerializer
 from raiden.tests.utils import factories
 from raiden.tests.utils.events import search_for_item
-from raiden.tests.utils.factories import UNIT_TOKEN_NETWORK_ADDRESS, UNIT_TRANSFER_AMOUNT
+from raiden.tests.utils.factories import HOP1_KEY, UNIT_TOKEN_NETWORK_ADDRESS, UNIT_TRANSFER_AMOUNT
 from raiden.transfer import views
 from raiden.transfer.architecture import TransitionResult
 from raiden.transfer.events import EventPaymentSentFailed
 from raiden.transfer.mediated_transfer import initiator_manager, mediator
-from raiden.transfer.mediated_transfer.events import SendLockedTransfer, SendRefundTransfer
+from raiden.transfer.mediated_transfer.events import SendLockedTransfer
 from raiden.transfer.mediated_transfer.state_change import (
     ActionTransferReroute,
     ReceiveTransferCancelRoute,
@@ -35,24 +38,26 @@ def test_can_create_refund_transfer_messages():
     assert len(refund_transfer.metadata.routes) == 1
 
 
-def test_route_metadata_hashing():
+def test_route_metadata_canonical():
     properties = factories.RouteMetadataProperties()
     one_route_metadata = factories.create(properties)
     assert isinstance(one_route_metadata, RouteMetadata)
-    one_hash = one_route_metadata.hash
+    one_canonical = one_route_metadata._canonical_dict()
 
     another_route_metadata = factories.create(properties)
-    another_hash = another_route_metadata.hash
+    another_canonical = another_route_metadata._canonical_dict()
 
-    assert one_hash == another_hash, "route metadata with same routes do not match"
+    assert one_canonical == another_canonical, "route metadata with same routes do not match"
 
     inverted_route_metadata = factories.create(
         factories.RouteMetadataProperties(route=[factories.HOP2, factories.HOP1])
     )
 
-    inverted_route_hash = inverted_route_metadata.hash
+    inverted_route_canonical = inverted_route_metadata._canonical_dict()
 
-    assert one_hash != inverted_route_hash, "route metadata with inverted routes still match"
+    assert (
+        one_canonical != inverted_route_canonical
+    ), "route metadata with inverted routes still match"
 
 
 def test_metadata_hashing():
@@ -79,6 +84,30 @@ def test_metadata_hashing():
     assert one_hash != inverted_route_hash, "route metadata with inverted routes still match"
 
 
+def test_route_metadata_displayname_validation():
+    properties = factories.RouteMetadataProperties()
+    one_metadata = factories.create(properties)
+    route = one_metadata.route
+    signer = LocalSigner(HOP1_KEY)
+    server_name = urlparse("https://ownserver.com").netloc
+    user_id = f"@{to_normalized_address(signer.address)}:{server_name}"
+    displayname = encode_hex(signer.sign(user_id.encode()))
+
+    bad_metadata = RouteMetadata(
+        route=route,
+        address_metadata={signer.address: {"user_id": user_id, "displayname": "fake"}},
+    )
+    good_metadata = RouteMetadata(
+        route=route,
+        address_metadata={signer.address: {"user_id": user_id, "displayname": displayname}},
+    )
+
+    assert bad_metadata.address_metadata == {}
+    assert good_metadata.address_metadata == {
+        signer.address: {"user_id": user_id, "displayname": displayname}
+    }
+
+
 def test_locked_transfer_with_metadata():
     locked_transfer = factories.create(factories.LockedTransferProperties())
     assert isinstance(locked_transfer, LockedTransfer)
@@ -450,7 +479,3 @@ def test_mediator_skips_used_routes():
     events = transition_result.events
     assert mediator_state is not None
     assert events
-
-    # no other routes available, so refund HOP1
-    assert isinstance(events[-1], SendRefundTransfer)
-    assert events[-1].recipient == factories.HOP1
diff --git a/raiden/tests/utils/app.py b/raiden/tests/utils/app.py
index c81408d10a..9d562590bc 100644
--- a/raiden/tests/utils/app.py
+++ b/raiden/tests/utils/app.py
@@ -5,7 +5,7 @@
 
 
 def database_from_privatekey(base_dir, app_number):
-    """ Format a database path based on the private key and app number. """
+    """Format a database path based on the private key and app number."""
     dbpath = os.path.join(base_dir, f"app{app_number}", f"v{RAIDEN_DB_VERSION}_log.db")
     os.makedirs(os.path.dirname(dbpath))
 
diff --git a/raiden/tests/utils/cli.py b/raiden/tests/utils/cli.py
index a0ad9c5156..bfc1a0dc19 100644
--- a/raiden/tests/utils/cli.py
+++ b/raiden/tests/utils/cli.py
@@ -1,7 +1,7 @@
 from typing import Any, Dict, Tuple
 from unittest import mock
 
-from click.core import ParameterSource  # type: ignore
+from click.core import ParameterSource
 
 from raiden.ui import cli
 
diff --git a/raiden/tests/utils/factories.py b/raiden/tests/utils/factories.py
index 42d8647abc..9de020da27 100644
--- a/raiden/tests/utils/factories.py
+++ b/raiden/tests/utils/factories.py
@@ -325,7 +325,7 @@ def make_privkey_address(
 
 
 def make_privkeys_ordered(count: int, reverse: bool = False) -> List[bytes]:
-    """ Return ``count`` private keys ordered by their respective address """
+    """Return ``count`` private keys ordered by their respective address"""
     key_address_pairs = [make_privkey_address() for _ in range(count)]
     return [key for key, _ in sorted(key_address_pairs, key=itemgetter(1), reverse=reverse)]
 
@@ -439,7 +439,7 @@ def _(properties, defaults=None):
 def make_canonical_identifier(
     chain_identifier=EMPTY, token_network_address=EMPTY, channel_identifier=EMPTY
 ) -> CanonicalIdentifier:
-    """ Alias of the CanonicalIdentifier create function """
+    """Alias of the CanonicalIdentifier create function"""
     return create(
         CanonicalIdentifierProperties(
             chain_identifier=chain_identifier,
@@ -626,7 +626,7 @@ class BalanceProofProperties(Properties):
 
     @property
     def balance_proof(self) -> "BalanceProofProperties":
-        """ Convenience method to extract balance proof properties from the child classes. """
+        """Convenience method to extract balance proof properties from the child classes."""
         return self.extract(BalanceProofProperties)
 
 
@@ -1158,7 +1158,7 @@ def get_hops(self, *args) -> List[HopState]:
     def get_route(
         self, channel_index: int, estimated_fee: FeeAmount = FeeAmount(0)  # noqa: B008
     ) -> RouteState:
-        """ Creates an *outbound* RouteState, based on channel our/partner addresses. """
+        """Creates an *outbound* RouteState, based on channel our/partner addresses."""
 
         channel = self.channels[channel_index]
         route = [channel.our_state.address, channel.partner_state.address]
diff --git a/raiden/tests/utils/mocks.py b/raiden/tests/utils/mocks.py
index 260ce8eb2b..4c0aa915f0 100644
--- a/raiden/tests/utils/mocks.py
+++ b/raiden/tests/utils/mocks.py
@@ -1,3 +1,4 @@
+import dataclasses
 import json
 import random
 from collections import defaultdict
@@ -7,29 +8,41 @@
 
 from raiden.api.objects import Notification
 from raiden.constants import Environment, RoutingMode
+from raiden.exceptions import PFSReturnedError
+from raiden.network.pathfinding import PFSConfig, PFSInfo
+from raiden.raiden_service import RaidenService
 from raiden.settings import RaidenConfig
 from raiden.storage.serialization import JSONSerializer
 from raiden.storage.sqlite import SerializedSQLiteStorage
 from raiden.storage.wal import WriteAheadLog
 from raiden.tests.utils import factories
 from raiden.tests.utils.factories import UNIT_CHAIN_ID
-from raiden.transfer import node
+from raiden.tests.utils.transfer import create_route_state_for_route
+from raiden.transfer import node, views
 from raiden.transfer.state import ChainState, NettingChannelState
+from raiden.transfer.state_change import Block
 from raiden.utils.keys import privatekey_to_address
 from raiden.utils.signer import LocalSigner
 from raiden.utils.typing import (
     Address,
+    AddressMetadata,
     BlockIdentifier,
     BlockNumber,
+    BlockTimeout,
     ChannelID,
     Dict,
+    List,
     Optional,
+    TokenAddress,
     TokenAmount,
     TokenNetworkAddress,
     TokenNetworkRegistryAddress,
+    Tuple,
 )
 from raiden_contracts.utils.type_aliases import ChainID
 
+RoutesDict = Dict[TokenAddress, Dict[Tuple[Address, Address], List[List[RaidenService]]]]
+
 
 class MockJSONRPCClient:
     def __init__(self, address: Address):
@@ -309,3 +322,101 @@ def chainId(self):
 class MockWeb3:
     def __init__(self, chain_id):
         self.eth = MockEth(chain_id=chain_id)
+
+
+class PFSMock:
+
+    PFSCONFIG_MAXIMUM_FEE = TokenAmount(100)
+    PFSCONFIG_IOU_TIMEOUT = BlockTimeout(5)
+    PFSCONFIG_MAX_PATHS = 5
+
+    def __init__(self, pfs_info: PFSInfo):
+        self.pfs_info: PFSInfo = pfs_info
+        self.address_to_address_metadata: Dict[Address, AddressMetadata] = dict()
+        self.routes: RoutesDict = defaultdict(lambda: defaultdict(list))
+
+    def get_pfs_info(self, url: str) -> PFSInfo:  # pylint: disable=unused-argument
+        return self.pfs_info
+
+    def on_new_block(self, block: Block):
+        self.update_info(confirmed_block_number=block.block_number)
+
+    def update_info(self, confirmed_block_number=None, price=None, matrix_server=None):
+        pfs_info_dict = dataclasses.asdict(self.pfs_info)
+        update_dict = dict(
+            confirmed_block_number=confirmed_block_number, price=price, matrix_server=matrix_server
+        )
+        update_dict = {k: v for k, v in update_dict.items() if v is not None}
+        pfs_info_dict.update(update_dict)
+        self.pfs_info = PFSInfo(**pfs_info_dict)
+
+    def query_address_metadata(self, pfs_config, user_address):  # pylint: disable=unused-argument
+        metadata = self.address_to_address_metadata.get(user_address)
+        if not metadata:
+            raise PFSReturnedError(message="Address not found", error_code=404, error_details={})
+        else:
+            return metadata
+
+    @staticmethod
+    def _get_app_address_metadata(app):
+        address = app.address
+        return address, app.transport.address_metadata
+
+    def add_apps(self, apps: List[RaidenService], add_pfs_config=True):
+        for app in apps:
+            address, metadata = self._get_app_address_metadata(app)
+            if not all((metadata.get("user_id"), address)):
+                raise AssertionError("Cant add app to PFSMock")
+
+            self.address_to_address_metadata[address] = metadata
+
+            # Add a default config to the node
+            if add_pfs_config is True:
+                app.config.pfs_config = PFSConfig(
+                    info=self.pfs_info,
+                    iou_timeout=self.PFSCONFIG_IOU_TIMEOUT,
+                    max_paths=self.PFSCONFIG_MAX_PATHS,
+                    maximum_fee=self.PFSCONFIG_MAXIMUM_FEE,
+                )
+
+    def set_route(self, token_address: TokenAddress, route: List[RaidenService]):
+        # Automatically observes from/to address from the route.
+        # This will not run any graph-like algorithms (include all possible paths along the route)
+        # and will only include the route from the start-app to the target-app
+        if len(route) > 1:
+            from_address = route[0].address
+            to_address = route[-1].address
+            self.routes[token_address][(from_address, to_address)].append(route)
+
+    def reset_routes(self, token_address: TokenAddress = None):
+        if token_address is None:
+            self.routes.clear()
+        else:
+            self.routes[token_address].clear()
+
+    def get_best_routes_pfs(  # pylint: disable=unused-argument
+        self,
+        chain_state,
+        token_network_address,
+        one_to_n_address,
+        from_address,
+        to_address,
+        amount,
+        previous_address,
+        pfs_config,
+        privkey,
+        pfs_wait_for_block,
+    ):
+        # simply get all routes from from_address to to_address as memorized in the self.routes
+        # datastructure, not regarding funds etc
+        token_network = views.get_token_network_by_address(chain_state, token_network_address)
+        if token_network is None:
+            return "No route found", [], None
+
+        token_address = token_network.token_address
+        routes_apps = self.routes[token_address][(from_address, to_address)]
+        if not routes_apps:
+            return "No route found", [], None
+
+        paths = [create_route_state_for_route(route, token_address) for route in routes_apps]
+        return None, paths, None
diff --git a/raiden/tests/utils/network.py b/raiden/tests/utils/network.py
index 7610ee9827..9c7bcddb39 100644
--- a/raiden/tests/utils/network.py
+++ b/raiden/tests/utils/network.py
@@ -156,7 +156,7 @@ def payment_channel_open_and_deposit(
     deposit: TokenAmount,
     settle_timeout: BlockTimeout,
 ) -> None:
-    """ Open a new channel with app0 and app1 as participants """
+    """Open a new channel with app0 and app1 as participants"""
     assert token_address
 
     block_identifier: BlockIdentifier
@@ -398,7 +398,6 @@ def create_apps(
     environment_type: Environment,
     unrecoverable_error_should_crash: bool,
     local_matrix_url: Optional[ParsedURL],
-    broadcast_rooms: List[str],
     routing_mode: RoutingMode,
     blockchain_query_interval: float,
     resolver_ports: List[Optional[int]],
@@ -406,7 +405,7 @@ def create_apps(
     port_generator: Iterator[Port],
     capabilities_config: CapabilitiesConfig,
 ) -> List[RaidenService]:
-    """ Create the apps."""
+    """Create the apps."""
     # pylint: disable=too-many-locals
     apps = []
     for idx, proxy_manager in enumerate(blockchain_services):
@@ -438,7 +437,6 @@ def create_apps(
 
         if local_matrix_url is not None:
             config.transport = MatrixTransportConfig(
-                broadcast_rooms=broadcast_rooms,
                 retries_before_backoff=retries_before_backoff,
                 retry_interval_initial=retry_interval_initial,
                 retry_interval_max=retry_interval_max,
@@ -657,12 +655,7 @@ def wait_for_channels(
     deposit: TokenAmount,
     retry_timeout: float = DEFAULT_RETRY_TIMEOUT,
 ) -> None:
-    """ Wait until all channels are usable from both directions. """
-    # XXX this is used a lot in the api / regression tests,
-    # this seems to fail around 80! tests
-
-    # This is because it calls wait for healthy internally.
-    # Maybe it's ok to just remove that
+    """Wait until all channels are usable from both directions."""
 
     for app0, app1 in app_channels:
         for token_address in token_addresses:
diff --git a/raiden/tests/utils/smartcontracts.py b/raiden/tests/utils/smartcontracts.py
index 15bbbfcac9..f1c46637d4 100644
--- a/raiden/tests/utils/smartcontracts.py
+++ b/raiden/tests/utils/smartcontracts.py
@@ -154,7 +154,7 @@ def compile_files_cwd(*args: Any, **kwargs: Any) -> Dict[str, Any]:
 
 
 def compile_test_smart_contract(name: str) -> Tuple[Dict[str, Any], str]:
-    """ Compiles the smart contract `name`. """
+    """Compiles the smart contract `name`."""
     contract_path = os.path.abspath(
         os.path.join(os.path.dirname(__file__), "..", "smart_contracts", f"{name}.sol")
     )
@@ -178,7 +178,7 @@ def deploy_rpc_test_contract(
 
 
 def get_list_of_block_numbers(item):
-    """ Creates a list of block numbers of the given list/single event"""
+    """Creates a list of block numbers of the given list/single event"""
     if isinstance(item, list):
         return [element["blockNumber"] for element in item]
 
diff --git a/raiden/tests/utils/smoketest.py b/raiden/tests/utils/smoketest.py
index d609f0987a..6f1eb1fead 100644
--- a/raiden/tests/utils/smoketest.py
+++ b/raiden/tests/utils/smoketest.py
@@ -23,7 +23,6 @@
 from raiden.accounts import AccountManager
 from raiden.constants import (
     BLOCK_ID_LATEST,
-    DISCOVERY_DEFAULT_ROOM,
     EMPTY_ADDRESS,
     GENESIS_BLOCK_NUMBER,
     SECONDS_PER_DAY,
@@ -34,7 +33,6 @@
 from raiden.network.proxies.proxy_manager import ProxyManager, ProxyManagerMetadata
 from raiden.network.proxies.user_deposit import UserDeposit
 from raiden.network.rpc.client import JSONRPCClient, make_sane_poa_middleware
-from raiden.network.transport.matrix.utils import make_room_alias
 from raiden.settings import DEFAULT_NUMBER_OF_BLOCK_CONFIRMATIONS, RAIDEN_CONTRACT_VERSION
 from raiden.tests.fixtures.constants import DEFAULT_BALANCE, DEFAULT_PASSPHRASE
 from raiden.tests.utils.eth_node import (
@@ -310,13 +308,14 @@ def setup_testchain(
 def setup_matrix_for_smoketest(
     print_step: StepPrinter,
     free_port_generator: Iterable[Port],
-    broadcast_rooms_aliases: Iterable[str],
 ) -> Iterator[List[Tuple["ParsedURL", HTTPExecutor]]]:
     from raiden.tests.utils.transport import matrix_server_starter
 
     print_step("Starting Matrix transport")
 
-    with matrix_server_starter(free_port_generator, broadcast_rooms_aliases) as ctx:
+    with matrix_server_starter(
+        free_port_generator,
+    ) as ctx:
         yield ctx
 
 
@@ -527,9 +526,6 @@ def setup_smoketest(
     matrix_manager = setup_matrix_for_smoketest(
         print_step=print_step,
         free_port_generator=free_port_generator,
-        broadcast_rooms_aliases=[
-            make_room_alias(CHAINNAME_TO_ID["smoketest"], DISCOVERY_DEFAULT_ROOM),
-        ],
     )
 
     # Do not redirect the stdout on a debug session, otherwise the REPL
diff --git a/raiden/tests/utils/transfer.py b/raiden/tests/utils/transfer.py
index a568bc823f..1005d1e77a 100644
--- a/raiden/tests/utils/transfer.py
+++ b/raiden/tests/utils/transfer.py
@@ -1,4 +1,6 @@
 """ Utilities to make and assert transfers. """
+import functools
+import itertools
 from contextlib import contextmanager, nullcontext
 from enum import Enum
 
@@ -58,7 +60,6 @@
     make_empty_pending_locks_state,
 )
 from raiden.transfer.state_change import ContractReceiveChannelDeposit, ReceiveUnlock
-from raiden.utils.capabilities import capconfig_to_dict
 from raiden.utils.formatting import to_checksum_address
 from raiden.utils.signer import LocalSigner, Signer
 from raiden.utils.timeout import BlockTimeout
@@ -79,13 +80,11 @@
     PaymentAmount,
     PaymentID,
     PaymentWithFeeAmount,
-    PeerCapabilities,
     SecretHash,
     TargetAddress,
     TokenAddress,
     TokenAmount,
     TokenNetworkAddress,
-    UserID,
     cast,
     typecheck,
 )
@@ -94,7 +93,7 @@
 
 
 class TransferState(Enum):
-    """ Represents the target state of a transfer. """
+    """Represents the target state of a transfer."""
 
     UNLOCKED = "unlocked"
     EXPIRED = "expired"
@@ -128,12 +127,7 @@ def create_route_state_for_route(
     address_metadata = dict()
     for app in apps:
         route.append(app.address)
-        user_id = UserID(app.transport.user_id)
-        capabilities = PeerCapabilities(
-            capconfig_to_dict(app.transport._config.capabilities_config)
-        )
-        assert user_id is not None
-        address_metadata[app.address] = {"user_id": user_id, "capabilities": capabilities}
+        address_metadata[app.address] = app.transport.address_metadata
 
     token_network = views.get_token_network_by_token_address(
         views.state_from_raiden(apps[0]),
@@ -151,6 +145,29 @@ def create_route_state_for_route(
         return RouteState(route=route, address_to_metadata=address_metadata)
 
 
+@contextmanager
+def patch_transfer_routes(routes: List[List[RaidenService]], token_address: TokenAddress):
+    """
+    Context manager to set specific routes for transfers.
+    This circumvents the lack of a PFS in the tests making a transfer fail.
+    """
+
+    apps = set(itertools.chain.from_iterable(routes))
+
+    for app in apps:
+        app.__mediated_transfer_async = app.mediated_transfer_async
+        route_states = [create_route_state_for_route(route, token_address) for route in routes]
+        app.mediated_transfer_async = functools.partial(
+            app.__mediated_transfer_async, route_states=route_states
+        )
+
+    yield
+
+    for app in apps:
+        app.mediated_transfer_async = app.__mediated_transfer_async
+        del app.__mediated_transfer_async
+
+
 @contextmanager
 def watch_for_unlock_failures(*apps):
     """
@@ -192,12 +209,12 @@ def transfer(
         Only the initiator and target are synced.
     """
 
+    route_states: Optional[List[RouteState]] = None
+    if routes:
+        route_states = list()
+        for route in routes:
+            route_states.append(create_route_state_for_route(route, token_address))
     if transfer_state is TransferState.UNLOCKED:
-        route_states: Optional[List[RouteState]] = None
-        if routes:
-            route_states = list()
-            for route in routes:
-                route_states.append(create_route_state_for_route(route, token_address))
         return _transfer_unlocked(
             initiator_app=initiator_app,
             target_app=target_app,
@@ -225,6 +242,7 @@ def transfer(
             amount=amount,
             identifier=identifier,
             timeout=timeout,
+            route_states=route_states,
         )
     else:
         raise RuntimeError("Type of transfer not implemented.")
@@ -339,6 +357,7 @@ def _transfer_secret_not_requested(
     amount: PaymentAmount,
     identifier: PaymentID,
     timeout: Optional[float] = None,
+    route_states: List[RouteState] = None,
 ) -> SecretHash:
     if timeout is None:
         timeout = 10
@@ -364,6 +383,7 @@ def _transfer_secret_not_requested(
         identifier=identifier,
         secret=secret,
         secrethash=secrethash,
+        route_states=route_states,
     )
 
     with Timeout(seconds=timeout):
@@ -877,7 +897,7 @@ def assert_succeeding_transfer_invariants(
     balance1: Balance,
     pending_locks1: List[HashTimeLockState],
 ) -> None:
-    """ Channels are in synced states and no unlock failures have occurred. """
+    """Channels are in synced states and no unlock failures have occurred."""
     assert not has_unlock_failure(app0)
     assert not has_unlock_failure(app1)
 
@@ -897,7 +917,7 @@ def wait_assert(func: Callable, *args, **kwargs) -> None:
             func(*args, **kwargs)
         except AssertionError as e:
             try:
-                gevent.sleep(0.5)
+                gevent.sleep(0.001)
             except gevent.Timeout:
                 raise e
         else:
@@ -905,7 +925,7 @@ def wait_assert(func: Callable, *args, **kwargs) -> None:
 
 
 def assert_mirror(original: NettingChannelState, mirror: NettingChannelState) -> None:
-    """ Assert that `mirror` has a correct `partner_state` to represent `original`."""
+    """Assert that `mirror` has a correct `partner_state` to represent `original`."""
     original_locked_amount = channel.get_amount_locked(original.our_state)
     mirror_locked_amount = channel.get_amount_locked(mirror.partner_state)
     assert original_locked_amount == mirror_locked_amount
@@ -926,7 +946,7 @@ def assert_mirror(original: NettingChannelState, mirror: NettingChannelState) ->
 def assert_locked(
     from_channel: NettingChannelState, pending_locks: List[HashTimeLockState]
 ) -> None:
-    """ Assert the locks created from `from_channel`. """
+    """Assert the locks created from `from_channel`."""
     # a locked transfer is registered in the _partner_ state
     if pending_locks:
         locks = PendingLocksState([lock.encoded for lock in pending_locks])
@@ -944,7 +964,7 @@ def assert_locked(
 def assert_balance(
     from_channel: NettingChannelState, balance: Balance, locked: LockedAmount
 ) -> None:
-    """ Assert the from_channel overall token values. """
+    """Assert the from_channel overall token values."""
     assert balance >= 0
     assert locked >= 0
 
diff --git a/raiden/tests/utils/transport.py b/raiden/tests/utils/transport.py
index baba9bc2fe..0d5801bd31 100644
--- a/raiden/tests/utils/transport.py
+++ b/raiden/tests/utils/transport.py
@@ -25,7 +25,7 @@
 
 from raiden.constants import DeviceIDs, Environment
 from raiden.messages.abstract import Message
-from raiden.network.transport.matrix.client import GMatrixClient, MatrixSyncMessages, Room
+from raiden.network.transport.matrix.client import GMatrixClient, MatrixSyncMessages
 from raiden.network.transport.matrix.transport import MatrixTransport, MessagesQueue
 from raiden.settings import MatrixTransportConfig
 from raiden.tests.utils.factories import make_signer
@@ -54,7 +54,6 @@ def get_admin_credentials(server_name):
 
 def new_client(
     handle_messages_callback: Callable[[MatrixSyncMessages], bool],
-    handle_member_join_callback: Callable[[Room], None],
     server: "ParsedURL",
 ) -> GMatrixClient:
     server_name = server.netloc
@@ -64,7 +63,6 @@ def new_client(
 
     client = GMatrixClient(
         handle_messages_callback=handle_messages_callback,
-        handle_member_join_callback=handle_member_join_callback,
         base_url=server,
     )
     client.login(username, password, sync=False)
@@ -72,10 +70,6 @@ def new_client(
     return client
 
 
-def ignore_member_join(_room: Room) -> None:
-    return None
-
-
 def ignore_messages(_matrix_messages: MatrixSyncMessages) -> bool:
     return True
 
@@ -100,45 +94,8 @@ def ignore_close(partner_address: Address) -> None:  # pylint: disable=unused-ar
     pass
 
 
-def setup_broadcast_room(servers: List["ParsedURL"], broadcast_room_name: str) -> None:
-    client = new_client(ignore_messages, ignore_member_join, servers[0])
-    admin_power_level = {"users": {client.user_id: 100}}
-    for server in servers:
-        admin_username = get_admin_credentials(server.netloc)["username"]
-        admin_user_id = f"@{admin_username}:{servers[0].netloc}"
-        admin_power_level["users"][admin_user_id] = 50
-
-    room = client.create_room(
-        alias=broadcast_room_name, is_public=True, power_level_content_override=admin_power_level
-    )
-
-    for server in servers[1:]:
-        client = new_client(ignore_messages, ignore_member_join, server)
-
-        # A user must join the room to create the room in the federated server
-        log.debug("canonical alias", alias=room.canonical_alias)
-        room = client.join_room(room.canonical_alias)
-
-        server_name = server.netloc
-        alias = f"#{broadcast_room_name}:{server_name}"
-
-        msg = "Setting up the room alias must not fail, otherwise the test can not run."
-        assert room.add_room_alias(alias), msg
-
-        all_aliases = client.api.get_aliases(room.room_id).get("aliases", [])
-
-        msg = "The new alias must be added, otherwise the Raiden node won't be able to find it."
-        assert alias in all_aliases, msg
-
-        msg = (
-            "Leaving the room failed. This is done otherwise there would be "
-            "a ghost user in the broadcast room"
-        )
-        assert room.leave() is None, msg
-
-
 class ParsedURL(str):
-    """ A string subclass that allows direct access to the split components of a URL """
+    """A string subclass that allows direct access to the split components of a URL"""
 
     def __new__(cls, *args, **kwargs):
         new = str.__new__(cls, *args, **kwargs)  # type: ignore
@@ -369,7 +326,6 @@ def generate_config(port: int) -> SynapseConfig:
 @contextmanager
 def matrix_server_starter(
     free_port_generator: Iterable[Port],
-    broadcast_rooms_aliases: Iterable[str],
     *,
     count: int = 1,
     config_generator: SynapseConfigGenerator = None,
@@ -444,13 +400,8 @@ def matrix_server_starter(
             # different, however the library doesn't support it. So here we
             # must poke at the private member and overwrite it.
             executor._timeout = teardown_timeout
-
             servers.append((server_url, executor))
 
-        log.debug("Setting up broadcast rooms", aliases=broadcast_rooms_aliases)
-        for broadcast_room_alias in broadcast_rooms_aliases:
-            setup_broadcast_room([url for url, _ in servers], broadcast_room_alias)
-
         yield servers
 
 
diff --git a/tools/debugging/stress_test_transfers.py b/tools/debugging/stress_test_transfers.py
index be7a1fa506..4474899dbe 100755
--- a/tools/debugging/stress_test_transfers.py
+++ b/tools/debugging/stress_test_transfers.py
@@ -203,7 +203,7 @@ def wait_for_status_ready(base_url: str, retry_timeout: int) -> None:
 def wait_for_reachable(
     transfers: List[TransferPath], token_address: str, retry_timeout: int
 ) -> None:
-    """ Wait until the nodes used for the transfers can see each other. """
+    """Wait until the nodes used for the transfers can see each other."""
 
     # Deduplicate the URLs for the channels which need reachability testing
     channels_not_reachable = set()