Repository: vegaprotocol/vegawallet
Pull request #482 (2022-02-08 09:30:56)

Release/v0.12.x


Patch:
diff --git a/.github/workflows/add_issue_new_projects.yml b/.github/workflows/add-issues-to-sprint-board.yml
similarity index 82%
rename from .github/workflows/add_issue_new_projects.yml
rename to .github/workflows/add-issues-to-sprint-board.yml
index a5c63aa..cb9d639 100644
--- a/.github/workflows/add_issue_new_projects.yml
+++ b/.github/workflows/add-issues-to-sprint-board.yml
@@ -1,16 +1,15 @@
----
+name: Add issue to sprint board
 
-name: Auto Assign Issue to New Project
-
-"on":
+on:
   issues:
-    types: [opened]
+    types:
+      - opened
 
 jobs:
-  add_issue:
+  add-to-sprint-board:
     runs-on: ubuntu-latest
     steps:
-      - name: Add Issue to New Core Project Board
+      - name: Add issue to sprint board
         env:
           GITHUB_TOKEN: ${{ secrets.GH_NEW_CARD_TO_PROJECT }}
           PROJECT_ID: ${{ secrets.CORE_PROJECT_ID }}
@@ -24,4 +23,3 @@ jobs:
                 }
               }
             }' -f project=$PROJECT_ID -f issue=$ISSUE_ID --jq '.data.addProjectNextItem.projectNextItem.id'
-
diff --git a/.github/workflows/go.yml b/.github/workflows/build-binaries.yml
similarity index 88%
rename from .github/workflows/go.yml
rename to .github/workflows/build-binaries.yml
index f98895c..30d1f21 100644
--- a/.github/workflows/go.yml
+++ b/.github/workflows/build-binaries.yml
@@ -1,10 +1,13 @@
-name: Go
+name: Build binaries
 
 on:
   push:
-    branches: [ master ]
+    branches:
+      - master
   pull_request:
-    branches: [ master, develop ]
+    branches:
+      - master
+      - develop
 
 jobs:
   build:
diff --git a/.github/workflows/docker.yml b/.github/workflows/build-docker-image.yml
similarity index 97%
rename from .github/workflows/docker.yml
rename to .github/workflows/build-docker-image.yml
index d5342c2..0626533 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/build-docker-image.yml
@@ -1,12 +1,9 @@
----
+name: Build Docker image
 
-name: Docker
-
-"on":
+on:
   pull_request:
     branches:
       - develop
-      - main
       - master
   push:
     branches:
@@ -16,7 +13,6 @@ name: Docker
       - "v[0-9]+.[0-9]+.[0-9]+-pre[0-9]+"
 
 jobs:
-
   build:
     runs-on: ubuntu-latest
     steps:
diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml
deleted file mode 100644
index f07b861..0000000
--- a/.github/workflows/golangci-lint.yml
+++ /dev/null
@@ -1,39 +0,0 @@
-name: golangci-lint
-on:
-  push:
-    tags:
-      - v*
-    branches:
-      - master
-      - main
-      - develop
-  pull_request:
-jobs:
-  golangci:
-    name: lint
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/checkout@v2
-      - name: golangci-lint
-        uses: golangci/golangci-lint-action@5c56cd6c9dc07901af25baab6f2b0d9f3b7c3018 # hash of v2.5.2
-        with:
-          # Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version
-          version: v1.42.1
-
-          # Optional: working directory, useful for monorepos
-          # working-directory: somedir
-
-          # Optional: golangci-lint command line arguments.
-          # args: --issues-exit-code=0
-
-          # Optional: show only new issues if it's a pull request. The default value is `false`.
-          # only-new-issues: true
-
-          # Optional: if set to true then the action will use pre-installed Go.
-          # skip-go-installation: true
-
-          # Optional: if set to true then the action don't cache or restore ~/go/pkg.
-          # skip-pkg-cache: true
-
-          # Optional: if set to true then the action don't cache or restore ~/.cache/go-build.
-          # skip-build-cache: true
diff --git a/.github/workflows/label-issues.yml b/.github/workflows/label-issues.yml
new file mode 100644
index 0000000..1d4922e
--- /dev/null
+++ b/.github/workflows/label-issues.yml
@@ -0,0 +1,18 @@
+name: Label issues
+
+on:
+  issues:
+    types:
+      - opened
+
+jobs:
+  label_issues:
+    runs-on: ubuntu-latest
+    permissions:
+      issues: write
+    steps:
+      - name: Add "wallet" label
+        uses: andymckay/labeler@5c59dabdfd4dd5bd9c6e6d255b01b9d764af4414
+        with:
+          add-labels: "wallet"
+          repo-token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/new_issues_to_sprint_project_board.yml b/.github/workflows/new_issues_to_sprint_project_board.yml
deleted file mode 100644
index c42946c..0000000
--- a/.github/workflows/new_issues_to_sprint_project_board.yml
+++ /dev/null
@@ -1,18 +0,0 @@
----
-
-name: Auto Assign New Issues to Project
-
-"on":
-  issues:
-    types: [opened]
-
-jobs:
-  assign_one_project:
-    runs-on: ubuntu-latest
-    name: Add new issues to Core Current Sprint project
-    steps:
-      - uses: alex-page/github-project-automation-plus@50502d399cbb98cefe7ce1f99f93f78c6756562e
-        with:
-          project: Core (Team Board)
-          column: Incoming to triage
-          repo-token: ${{ secrets.GH_NEW_CARD_TO_PROJECT }}
diff --git a/.github/workflows/release-binaries.yml b/.github/workflows/release-binaries.yml
new file mode 100644
index 0000000..1a8b828
--- /dev/null
+++ b/.github/workflows/release-binaries.yml
@@ -0,0 +1,251 @@
+name: Release binaries
+
+on:
+  push:
+    # Sequence of patterns matched against refs/tags
+    tags:
+      - "v*" # Push events to matching v*, i.e. v1.0, v20.15.10
+
+jobs:
+  check-version:
+    name: Verify hard-coded version
+    runs-on: ubuntu-latest
+    steps:
+      - name: Check out code
+        uses: actions/checkout@v2
+
+      - name: Verify hard-coded version matches the tag
+        run: |
+          # Filter the content version/version.go to find the `defaultVersion`
+          # constant with its line number. It should look like this:
+          #     13: defaultVersion = v0.12.0+dev
+          #     ^ the line number in version/version.go
+          #         ^ the constant
+          #                          ^ the hard-coded version
+          version_info=$(grep -n "defaultVersion\s*=" version/version.go | xargs)
+
+          echo "Found info: ${version_info}"
+
+          # Extract the version from the version_info found.
+          #   - awk '{print $4}'
+          #       get the 4th segment in the version_info output, the hard-coded
+          #       version.
+          #   - xargs
+          #       trim the output of spaces and quotes.
+          version=$(echo $version_info | awk '{print $4}' | xargs )
+
+          echo "Found version: ${version}"
+
+          # Get the line at which the version is set in version.go
+          #   - awk '{print $1}'
+          #       get the 1st segment in the version_info output, the line number.
+          #   - cut -d: -f1
+          #       get rid of the colon (`:`)
+          line=$(echo $version_info | awk '{print $1}' | cut -d: -f1)
+
+          echo "Found line: ${line}"
+          
+          if [ "$version" = "${{github.ref_name}}" ]; then
+            echo "The hard-coded version matches the tag."
+          else
+            echo "::error file=version/version.go,line=$line::The hard-coded version \"$version\" does not match the tag \"${{github.ref_name}}\". Please, update the hard-coded version."
+            exit 1
+          fi
+
+  release-linux:
+    needs: check-version
+    name: Release on Linux ${{ matrix.arch }}
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        arch:
+          - amd64
+          - arm64
+    env:
+      GOOS: linux
+      GOARCH: ${{ matrix.arch }}
+      CGO_ENABLED: 0
+    steps:
+      - name: Set up Go
+        uses: actions/setup-go@v2
+        with:
+          go-version: 1.17
+        id: go
+
+      - name: Check out code
+        uses: actions/checkout@v2
+
+      - name: Get dependencies
+        run: go get -v -t -d ./...
+
+      - name: Run tests
+        if: ${{ env.GOARCH == 'amd64' }}
+        run: go test -v ./...
+
+      - name: Build binary
+        run: |
+          hash=$(echo ${{ github.sha }} | cut -b1-8)
+          go build -o build/vegawallet -ldflags "-X code.vegaprotocol.io/vegawallet/version.VersionHash=$hash"
+
+      - name: Bundle binary in archive
+        uses: thedoctor0/zip-release@master
+        with:
+          type: zip
+          directory: build
+          filename: vegawallet-${{ env.GOOS }}-${{ env.GOARCH }}.zip
+
+      - name: Release
+        uses: softprops/action-gh-release@v1
+        with:
+          files: build/*.zip
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+  release-macos:
+    needs: check-version
+    name: Release on MacOS ${{ matrix.arch }}
+    runs-on: macos-latest
+    strategy:
+      matrix:
+        arch:
+          - amd64
+          - arm64
+    env:
+      GOOS: darwin
+      GOARCH: ${{ matrix.arch }}
+      CGO_ENABLED: 0
+    steps:
+      - name: Set up Go
+        uses: actions/setup-go@v2
+
+        with:
+          go-version: 1.17
+        id: go
+
+      - name: Check out code
+        uses: actions/checkout@v2
+
+      - name: Get dependencies
+        run: go get -v -t -d ./...
+
+      - name: Run tests
+        if: ${{ env.GOARCH == 'amd64' }}
+        run: go test -v ./...
+
+      - name: Build binary
+        run: |
+          hash=$(echo ${{ github.sha }} | cut -b1-8)
+          go build -o build/vegawallet -ldflags "-X code.vegaprotocol.io/vegawallet/version.VersionHash=$hash"
+
+      - name: Import DeveloperID Certificate
+        uses: apple-actions/import-codesign-certs@v1
+        with:
+          keychain: vega
+          create-keychain: true
+          p12-file-base64: ${{ secrets.MACOS_CERTIFICATE }}
+          p12-password: ${{ secrets.MACOS_CERTIFICATE_PASS }}
+
+      - name: Sign binary
+        working-directory: build
+        # --timestamp
+        #     During signing, requests that a timestamp authority server be contacted to authenticate the time of
+        #     signing.
+        # --deep
+        #     When signing a bundle, specifies that nested code content such as helpers, frameworks, and plug-ins,
+        #     should be recursively signed in turn.
+        # --options runtime
+        #     On macOS versions >= 10.14.0, opts signed processes into a hardened runtime environment which includes
+        #     runtime code signing enforcement, library validation, hard, kill, and debugging restrictions.
+        run: codesign --verbose --sign "${{ secrets.MACOS_CERTIFICATE_IDENTITY_ID }}" --timestamp --options runtime --deep --force vegawallet
+
+      - name: Verify signature
+        working-directory: build
+        run: codesign --verbose --verify --strict --deep vegawallet
+
+      - name: Bundle binary in archive
+        uses: thedoctor0/zip-release@master
+        with:
+          type: zip
+          directory: build
+          filename: vegawallet-${{ env.GOOS }}-${{ env.GOARCH }}.zip
+
+      - name: Store notarization credentials
+        run: |
+          xcrun notarytool store-credentials vega \
+            --apple-id "${{ secrets.MACOS_NOTARIZATION_APPLE_ID }}" \
+            --team-id "${{ secrets.MACOS_NOTARIZATION_TEAM_ID }}" \
+            --password "${{ secrets.MACOS_NOTARIZATION_PASS }}"
+
+      - name: Notarize app
+        working-directory: build
+        run: |
+          xcrun notarytool submit vegawallet-${{ env.GOOS }}-${{ env.GOARCH }}.zip \
+            --keychain-profile vega \
+            --output-format json \
+            --timeout "90m" \
+            --wait
+
+      - name: Release
+        uses: softprops/action-gh-release@v1
+        with:
+          files: build/*.zip
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+  release-windows:
+    needs: check-version
+    name: Release on Windows ${{ matrix.arch }}
+    runs-on: windows-latest
+    strategy:
+      matrix:
+        arch:
+          - amd64
+          - arm64
+    env:
+      GOOS: windows
+      GOARCH: ${{ matrix.arch }}
+      CGO_ENABLED: 0
+    steps:
+      - name: Set up Go
+        uses: actions/setup-go@v2
+        with:
+          go-version: 1.17
+        id: go
+
+      - name: Check out code
+        uses: actions/checkout@v2
+
+      - name: Get dependencies
+        run: go get -v -t -d ./...
+
+      - name: Run tests
+        if: ${{ env.GOARCH == 'amd64' }}
+        run: go test -v ./...
+
+      - name: Build binary
+        run: |
+          $hash= "${{ github.sha }}".substring(0,8)
+          go build -o build/vegawallet.exe -ldflags "-X code.vegaprotocol.io/vegawallet/version.VersionHash=$hash"
+
+      - name: "Sign binary"
+        uses: Dana-Prajea/code-sign-action@98c79121b376beab8d6a9484f445089db4461bca
+        with:
+          certificate: ${{ secrets.WINDOWS_CERTIFICATE }}
+          password: ${{ secrets.WINDOWS_CERTIFICATE_PASS }}
+          certificatesha1: ${{ secrets.WINDOWS_CERTIFICATE_HASH }}
+          folder: "build"
+          timestampUrl: "http://timestamp.sectigo.com"
+
+      - name: Bundle binary in archive
+        uses: thedoctor0/zip-release@master
+        with:
+          type: zip
+          directory: build
+          filename: vegawallet-${{ env.GOOS }}-${{ env.GOARCH }}.zip
+
+      - name: Release
+        uses: softprops/action-gh-release@v1
+        with:
+          files: build/*.zip
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
deleted file mode 100644
index 26a8496..0000000
--- a/.github/workflows/release.yml
+++ /dev/null
@@ -1,58 +0,0 @@
-name: Release
-
-on:
-  push:
-    # Sequence of patterns matched against refs/tags
-    tags:
-      - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
-
-jobs:
-  release:
-    name: Release on ${{ matrix.os }} ${{ matrix.arch }}
-    runs-on: ${{ matrix.os }}
-    strategy:
-      matrix:
-        os: [ macos-latest, windows-latest, ubuntu-latest ]
-        arch: [ amd64, arm64 ]
-        include:
-          - os: macos-latest
-            platform: macos
-          - os: windows-latest
-            platform: windows
-          - os: ubuntu-latest
-            platform: linux
-    env:
-      GOARCH: ${{ matrix.arch }}
-    steps:
-      - name: Set up Go
-        uses: actions/setup-go@v2
-        with:
-          go-version: 1.17
-        id: go
-
-      - name: Check out code into the Go module directory
-        uses: actions/checkout@v2
-
-      - name: Get dependencies
-        run: |
-          go get -v -t -d ./...
-
-      - name: Tests
-        if: ${{ matrix.arch == 'amd64' }}
-        run: go test -v ./...
-
-      - name: Set release version
-        id: vars
-        run: echo ::set-output name=tag::${GITHUB_REF#refs/*/}
-
-      - name: Build
-        env:
-          RELEASE_VERSION: ${{ steps.vars.outputs.tag }}
-        run: make release-${{ matrix.platform }}-${{ matrix.arch }}
-
-      - name: Release
-        uses: softprops/action-gh-release@v1
-        with:
-          files: build/*.zip
-        env:
-          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/search-for-code-offences.yml b/.github/workflows/search-for-code-offences.yml
new file mode 100644
index 0000000..1125b26
--- /dev/null
+++ b/.github/workflows/search-for-code-offences.yml
@@ -0,0 +1,21 @@
+name: Search for code offences
+
+on:
+  push:
+    tags:
+      - v*
+    branches:
+      - master
+      - develop
+  pull_request:
+
+jobs:
+  golangci:
+    name: Run golangci-lint
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v2
+      - name: golangci-lint
+        uses: golangci/golangci-lint-action@5c56cd6c9dc07901af25baab6f2b0d9f3b7c3018 # hash of v2.5.2
+        with:
+          version: v1.42.1
diff --git a/.github/workflows/changelog-checker.yml b/.github/workflows/verify-changelog-is-updated.yml
similarity index 82%
rename from .github/workflows/changelog-checker.yml
rename to .github/workflows/verify-changelog-is-updated.yml
index 1c3a3ec..a42ee94 100644
--- a/.github/workflows/changelog-checker.yml
+++ b/.github/workflows/verify-changelog-is-updated.yml
@@ -1,14 +1,15 @@
-name: ChangelogChecker
+name: Verify CHANGELOG is updated
+
 on:
   pull_request:
-    types: [review_requested, ready_for_review]
     branches:
-      - develop
       - master
+      - develop
+
 jobs:
-  build:
+  changelog-checker:
     if: startsWith(github.head_ref, 'renovate/') != true
-    name: Check Actions
+    name: Run changelog-checker
     runs-on: ubuntu-latest
     steps:
       - uses: actions/checkout@v1
diff --git a/.github/workflows/pr_verify_linked_issue.yml b/.github/workflows/verify-pr-has-linked-issue.yml
similarity index 51%
rename from .github/workflows/pr_verify_linked_issue.yml
rename to .github/workflows/verify-pr-has-linked-issue.yml
index 493a249..ce103aa 100644
--- a/.github/workflows/pr_verify_linked_issue.yml
+++ b/.github/workflows/verify-pr-has-linked-issue.yml
@@ -1,17 +1,16 @@
----
+name: Verify PR has a linked issue
 
-# This workflow will inspect a pull request to ensure there is a linked issue or a
-# valid issue is mentioned in the body. If neither is present it fails the check and adds
-# a comment alerting users of this missing requirement. Edit to test.
-name: VerifyIssue
-
-"on":
+on:
   pull_request:
-    types: [edited, synchronize, opened, reopened]
+    types:
+      - edited
+      - synchronize
+      - opened
+      - reopened
   check_run:
 
 jobs:
-  verify_linked_issue:
+  verify-linked-issue:
     if: startsWith(github.head_ref, 'renovate/') != true
     runs-on: ubuntu-latest
     name: Ensure Pull Request has a linked issue.
diff --git a/.gitignore b/.gitignore
index a9d5622..a829405 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,7 @@ vegawallet
 
 # Golang
 vendor/
+build/
 
 # MacOS
 .DS_Store
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 23c981f..4e7f4a6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,6 @@
 # Changelog
 
-## Unreleased (0.12.0)
+## Unreleased (0.13.0)
 
 ### 🚨 Breaking changes
 - [](https://github.com/vegaprotocol/vegawallet/pull/) -
@@ -18,6 +18,40 @@
 - [](https://github.com/vegaprotocol/vegawallet/pull/) -
 
 
+## 0.12.0
+
+### 🚨 Breaking changes
+- [446](https://github.com/vegaprotocol/vegawallet/pull/446) - The key printed on the JSON output for the `key generate` command is now at the root of the response, just like the output of the `key describe` command.
+- [450](https://github.com/vegaprotocol/vegawallet/pull/450) - Move `sign` and `verify` commands to a a `message` subcommand, e.g. `message sign` and `message verify`
+
+### 🗑️  Deprecation
+- [](https://github.com/vegaprotocol/vegawallet/pull/) -
+
+### 🛠  Improvements
+- [427](https://github.com/vegaprotocol/vegawallet/pull/427) - Sign MacOS binaries
+- [447](https://github.com/vegaprotocol/vegawallet/pull/447) - Sign Windows binaries
+- [448](https://github.com/vegaprotocol/vegawallet/pull/448) - Add command `network locate` to locate the folder in which network configuration files are stored.
+- [451](https://github.com/vegaprotocol/vegawallet/pull/451) - Verify network configuration prior exploitation to raise user-friendly errors.
+- [435](https://github.com/vegaprotocol/vegawallet/pull/435) - Support signing commands offline with `command sign`
+- [453](https://github.com/vegaprotocol/vegawallet/pull/453) - Added support for new liquidity provision commands
+- [456](https://github.com/vegaprotocol/vegawallet/pull/456) - Update protos to support non-negative string pegged orders offset
+- [463](https://github.com/vegaprotocol/vegawallet/pull/463) - Improve the `vegawallet version` command output to ensure users doesn't mistake the software version with their wallet version
+- [443](https://github.com/vegaprotocol/vegawallet/pull/443) - Notarization of MacOS binaries
+- [469](https://github.com/vegaprotocol/vegawallet/pull/469) - Support time-triggered trading termination from built-in oracle
+
+### 🔥 Removal
+- [446](https://github.com/vegaprotocol/vegawallet/pull/446) - Wallet creation with `key generate` has been removed in favor of `create` command
+- [446](https://github.com/vegaprotocol/vegawallet/pull/446) - Flag `--console-proxy` has been removed in favor of `--with-console`
+- [446](https://github.com/vegaprotocol/vegawallet/pull/446) - Flag `--mnemonic-file` has been removed in favor of `--recovery-phrase-file`
+- [446](https://github.com/vegaprotocol/vegawallet/pull/446) - Field `mnemonic` in HTTP response for wallet creation has been removed in favor of `recoveryPhrase`
+- [446](https://github.com/vegaprotocol/vegawallet/pull/446) - Field `mnemonic` in HTTP request for wallet import has been removed in favor of `recoveryPhrase`
+
+### 🐛 Fixes
+- [440](https://github.com/vegaprotocol/vegawallet/pull/440) - Fix confirmation prompt on windows
+- [449](https://github.com/vegaprotocol/vegawallet/pull/449) - Don't use unicode glyphs on windows
+- [444](https://github.com/vegaprotocol/vegawallet/pull/444) - Fail gracefully when trying to get password input on msys
+- [455](https://github.com/vegaprotocol/vegawallet/pull/455) - New Liquidity Provisioning commands removed requirement for Id field
+
 ## 0.11.0
 
 ### 🚨 Breaking changes
diff --git a/CODEOWNERS b/CODEOWNERS
index 2c4a130..6b13d51 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -1,2 +1,1 @@
-*.md @edd
-* @jeremyletang
+*  @vegaprotocol/core
diff --git a/cmd/cmd.go b/cmd/cmd.go
index 7b49b35..dc05a54 100644
--- a/cmd/cmd.go
+++ b/cmd/cmd.go
@@ -7,10 +7,10 @@ import (
 	"os"
 	"time"
 
+	vgterm "code.vegaprotocol.io/shared/libs/term"
 	"code.vegaprotocol.io/shared/paths"
 	"code.vegaprotocol.io/vegawallet/cmd/flags"
 	"code.vegaprotocol.io/vegawallet/cmd/printer"
-	vgterm "code.vegaprotocol.io/vegawallet/libs/term"
 	netstore "code.vegaprotocol.io/vegawallet/network/store/v1"
 	"code.vegaprotocol.io/vegawallet/wallet"
 	"code.vegaprotocol.io/vegawallet/wallets"
@@ -22,8 +22,6 @@ const (
 	ForwarderRequestTimeout    = 5 * time.Second
 )
 
-var ErrNetworkDoesNotHaveGRPCHostConfigured = errors.New("network does not have gRPC hosts configured")
-
 type Error struct {
 	Err string `json:"error"`
 }
diff --git a/cmd/command.go b/cmd/command.go
index c0eb471..5bda607 100644
--- a/cmd/command.go
+++ b/cmd/command.go
@@ -14,5 +14,6 @@ func NewCmdCommand(w io.Writer, rf *RootFlags) *cobra.Command {
 	}
 
 	cmd.AddCommand(NewCmdCommandSend(w, rf))
+	cmd.AddCommand(NewCmdCommandSign(w, rf))
 	return cmd
 }
diff --git a/cmd/command_send.go b/cmd/command_send.go
index e27edd5..57184eb 100644
--- a/cmd/command_send.go
+++ b/cmd/command_send.go
@@ -8,11 +8,11 @@ import (
 
 	api "code.vegaprotocol.io/protos/vega/api/v1"
 	walletpb "code.vegaprotocol.io/protos/vega/wallet/v1"
+	vglog "code.vegaprotocol.io/shared/libs/zap"
 	"code.vegaprotocol.io/vegawallet/cmd/cli"
 	"code.vegaprotocol.io/vegawallet/cmd/flags"
 	"code.vegaprotocol.io/vegawallet/cmd/printer"
 	wcommands "code.vegaprotocol.io/vegawallet/commands"
-	vglog "code.vegaprotocol.io/vegawallet/libs/zap"
 	"code.vegaprotocol.io/vegawallet/network"
 	"code.vegaprotocol.io/vegawallet/node"
 	"code.vegaprotocol.io/vegawallet/wallets"
diff --git a/cmd/command_sign.go b/cmd/command_sign.go
new file mode 100644
index 0000000..904ef8b
--- /dev/null
+++ b/cmd/command_sign.go
@@ -0,0 +1,182 @@
+package cmd
+
+import (
+	"fmt"
+	"io"
+	"os"
+
+	walletpb "code.vegaprotocol.io/protos/vega/wallet/v1"
+	"code.vegaprotocol.io/vegawallet/cmd/cli"
+	"code.vegaprotocol.io/vegawallet/cmd/flags"
+	"code.vegaprotocol.io/vegawallet/cmd/printer"
+	wcommands "code.vegaprotocol.io/vegawallet/commands"
+	"code.vegaprotocol.io/vegawallet/wallet"
+	"code.vegaprotocol.io/vegawallet/wallets"
+	"github.com/golang/protobuf/jsonpb"
+	"github.com/spf13/cobra"
+)
+
+var (
+	signCommandLong = cli.LongDesc(`
+		Sign a command using the specified wallet and public key and bundle it as a
+		transaction ready to be sent. The resulting transaction is base64-encoded and
+		can be sent using the command "tx send".
+
+		The command should be a Vega command formatted as a JSON payload, as follows:
+
+		'{"commandName": {"someProperty": "someValue"} }'
+
+		For vote submission, it will look like this:
+
+		'{"voteSubmission": {"proposalId": "some-id", "value": "VALUE_YES"}}'
+	`)
+
+	signCommandExample = cli.Examples(`
+		# Sign a command
+		vegawallet command sign --wallet WALLET --pubkey PUBKEY --tx-height TX_HEIGHT COMMAND
+
+		# To decode the result, save the result in a file and use the command 
+		# "base64"
+		vegawallet command sign --wallet WALLET --pubkey PUBKEY --tx-height TX_HEIGHT COMMAND > result.txt
+		base64 --decode --input result.txt
+	`)
+)
+
+type SignCommandHandler func(*wallet.SignCommandRequest) (*wallet.SignCommandResponse, error)
+
+func NewCmdCommandSign(w io.Writer, rf *RootFlags) *cobra.Command {
+	handler := func(req *wallet.SignCommandRequest) (*wallet.SignCommandResponse, error) {
+		store, err := wallets.InitialiseStore(rf.Home)
+		if err != nil {
+			return nil, fmt.Errorf("couldn't initialise wallets store: %w", err)
+		}
+
+		return wallet.SignCommand(store, req)
+	}
+
+	return BuildCmdCommandSign(w, handler, rf)
+}
+
+func BuildCmdCommandSign(w io.Writer, handler SignCommandHandler, rf *RootFlags) *cobra.Command {
+	f := &SignCommandFlags{}
+
+	cmd := &cobra.Command{
+		Use:     "sign",
+		Short:   "Sign a command for offline use",
+		Long:    signCommandLong,
+		Example: signCommandExample,
+		RunE: func(_ *cobra.Command, args []string) error {
+			if aLen := len(args); aLen == 0 {
+				return flags.ArgMustBeSpecifiedError("command")
+			} else if aLen > 1 {
+				return flags.TooManyArgsError("command")
+			}
+			f.RawCommand = args[0]
+
+			req, err := f.Validate()
+			if err != nil {
+				return err
+			}
+
+			resp, err := handler(req)
+			if err != nil {
+				return err
+			}
+
+			switch rf.Output {
+			case flags.InteractiveOutput:
+				PrintSignCommandResponse(w, resp)
+			case flags.JSONOutput:
+				return printer.FprintJSON(w, resp)
+			}
+
+			return nil
+		},
+	}
+
+	cmd.Flags().StringVarP(&f.Wallet,
+		"wallet", "w",
+		"",
+		"Wallet holding the public key",
+	)
+	cmd.Flags().StringVarP(&f.PubKey,
+		"pubkey", "k",
+		"",
+		"Public key of the key pair to use for signing (hex-encoded)",
+	)
+	cmd.Flags().StringVarP(&f.PassphraseFile,
+		"passphrase-file", "p",
+		"",
+		"Path to the file containing the wallet's passphrase",
+	)
+	cmd.Flags().Uint64Var(&f.TxBlockHeight,
+		"tx-height",
+		0,
+		"It should be close to the current block height when the transaction is applied, with a threshold of ~ - 150 blocks.",
+	)
+
+	autoCompleteWallet(cmd, rf.Home)
+
+	return cmd
+}
+
+type SignCommandFlags struct {
+	Wallet         string
+	PubKey         string
+	PassphraseFile string
+	RawCommand     string
+	TxBlockHeight  uint64
+}
+
+func (f *SignCommandFlags) Validate() (*wallet.SignCommandRequest, error) {
+	req := &wallet.SignCommandRequest{}
+
+	if len(f.Wallet) == 0 {
+		return nil, flags.FlagMustBeSpecifiedError("wallet")
+	}
+	req.Wallet = f.Wallet
+
+	passphrase, err := flags.GetPassphrase(f.PassphraseFile)
+	if err != nil {
+		return nil, err
+	}
+	req.Passphrase = passphrase
+
+	if f.TxBlockHeight == 0 {
+		return nil, flags.FlagMustBeSpecifiedError("tx-height")
+	}
+	req.TxBlockHeight = f.TxBlockHeight
+
+	if len(f.PubKey) == 0 {
+		return nil, flags.FlagMustBeSpecifiedError("pubkey")
+	}
+	if len(f.RawCommand) == 0 {
+		return nil, flags.ArgMustBeSpecifiedError("command")
+	}
+	request := &walletpb.SubmitTransactionRequest{}
+	if err := jsonpb.UnmarshalString(f.RawCommand, request); err != nil {
+		return nil, fmt.Errorf("couldn't unmarshal command as request: %w", err)
+	}
+	if len(request.PubKey) != 0 {
+		return nil, ErrDoNotSetPubKeyInCommand
+	}
+	request.PubKey = f.PubKey
+	request.Propagate = true
+	req.Request = request
+	if errs := wcommands.CheckSubmitTransactionRequest(req.Request); !errs.Empty() {
+		return nil, fmt.Errorf("invalid request: %w", errs)
+	}
+
+	return req, nil
+}
+
+func PrintSignCommandResponse(w io.Writer, req *wallet.SignCommandResponse) {
+	p := printer.NewInteractivePrinter(w)
+
+	p.CheckMark().SuccessText("Command signature successful").NextSection()
+	p.Text("Transaction (base64-encoded):").NextLine().WarningText(req.Base64Transaction).NextSection()
+
+	p.BlueArrow().InfoText("Send a transaction").NextLine()
+	p.Text("To send a raw transaction, see the following command:").NextSection()
+	p.Code(fmt.Sprintf("%s tx send --help", os.Args[0])).NextSection()
+}
diff --git a/cmd/flags/force.go b/cmd/flags/force.go
index fe9ef8c..97816ae 100644
--- a/cmd/flags/force.go
+++ b/cmd/flags/force.go
@@ -19,7 +19,7 @@ func DoYouConfirm() (bool, error) {
 	}
 	fmt.Println() //nolint:forbidigo
 
-	answer = strings.ToLower(strings.Trim(answer, " \n\t"))
+	answer = strings.ToLower(strings.Trim(answer, " \r\n\t"))
 
 	switch answer {
 	case "yes", "y":
diff --git a/cmd/flags/passphrase.go b/cmd/flags/passphrase.go
index a2605ac..5777970 100644
--- a/cmd/flags/passphrase.go
+++ b/cmd/flags/passphrase.go
@@ -7,7 +7,7 @@ import (
 	"strings"
 
 	vgfs "code.vegaprotocol.io/shared/libs/fs"
-	vgterm "code.vegaprotocol.io/vegawallet/libs/term"
+	vgterm "code.vegaprotocol.io/shared/libs/term"
 	"golang.org/x/term"
 )
 
@@ -15,6 +15,7 @@ var (
 	ErrPassphraseRequiredWithoutTTY = errors.New("passphrase flag is required without TTY")
 	ErrPassphraseDoNotMatch         = errors.New("passphrases do not match")
 	ErrPassphraseMustBeSpecified    = errors.New("passphrase must be specified")
+	ErrMsysPasswordInput            = errors.New("password input is not supported on msys (use --passphrase-file or a standard windows terminal)")
 )
 
 type PassphraseGetterWithOps func(bool) (string, error)
@@ -102,7 +103,15 @@ func ReadPassphraseInputWithOpts(withConfirmation bool) (string, error) {
 	return passphrase, nil
 }
 
+func runningInMsys() bool {
+	ms := os.Getenv("MSYSTEM")
+	return ms != ""
+}
+
 func promptForPassphrase(msg ...string) (string, error) {
+	if runningInMsys() {
+		return "", ErrMsysPasswordInput
+	}
 	fmt.Print(msg[0]) //nolint:forbidigo
 	password, err := term.ReadPassword(int(os.Stdin.Fd()))
 	if err != nil {
diff --git a/cmd/key_generate.go b/cmd/key_generate.go
index 4d15cf7..2005423 100644
--- a/cmd/key_generate.go
+++ b/cmd/key_generate.go
@@ -15,10 +15,7 @@ import (
 
 var (
 	generateKeyLong = cli.LongDesc(`
-		Generate a new Ed25519 key pair in a given wallet.
-
-		DEPRECATED:
-		If the targeted wallet doesn't exist, it will be automatically generated.
+		Generate a new Ed25519 key pair in the specified wallet.
 	`)
 
 	generateKeyExample = cli.Examples(`
@@ -33,23 +30,15 @@ var (
 	`)
 )
 
-type GenerateKeyHandler func(flags.PassphraseGetterWithOps, *wallet.GenerateKeyRequest) (*wallet.GenerateKeyResponse, error)
+type GenerateKeyHandler func(*wallet.GenerateKeyRequest) (*wallet.GenerateKeyResponse, error)
 
 func NewCmdGenerateKey(w io.Writer, rf *RootFlags) *cobra.Command {
-	h := func(passphraseGetter flags.PassphraseGetterWithOps, req *wallet.GenerateKeyRequest) (*wallet.GenerateKeyResponse, error) {
+	h := func(req *wallet.GenerateKeyRequest) (*wallet.GenerateKeyResponse, error) {
 		s, err := wallets.InitialiseStore(rf.Home)
 		if err != nil {
 			return nil, fmt.Errorf("couldn't initialise wallets store: %w", err)
 		}
 
-		// Because the passphrase needs to be retrieved based on the state of the
-		// specified wallet, we get it here and fill the request with it.
-		passphrase, err := passphraseGetter(!s.WalletExists(req.Wallet))
-		if err != nil {
-			return nil, err
-		}
-		req.Passphrase = passphrase
-
 		return wallet.GenerateKey(s, req)
 	}
 
@@ -70,28 +59,14 @@ func BuildCmdGenerateKey(w io.Writer, handler GenerateKeyHandler, rf *RootFlags)
 				return err
 			}
 
-			// If the specified wallet doesn't exist yet, we need to ask for
-			// passphrase confirmation. However, this information is only available
-			// to the handler, which means we can't retrieve the passphrase during
-			// the flag validation step.
-			//
-			// As a result, we need to delegate the retrieval of the passphrase
-			// to the handler. This is why we build a function that takes care
-			// of this task based on the flags set, and pass it to the handler.
-			//
-			// With this method, the handler can get the passphrase in isolation,
-			// without knowledge of the command line flags nor the retrieval
-			// process.
-			pg := flags.BuildPassphraseGetterWithOps(f.PassphraseFile)
-
-			resp, err := handler(pg, req)
+			resp, err := handler(req)
 			if err != nil {
 				return err
 			}
 
 			switch rf.Output {
 			case flags.InteractiveOutput:
-				PrintGenerateKeyResponse(w, resp)
+				PrintGenerateKeyResponse(w, req, resp)
 			case flags.JSONOutput:
 				return printer.FprintJSON(w, resp)
 			}
@@ -141,39 +116,27 @@ func (f *GenerateKeyFlags) Validate() (*wallet.GenerateKeyRequest, error) {
 	}
 	req.Metadata = metadata
 
+	passphrase, err := flags.GetPassphrase(f.PassphraseFile)
+	if err != nil {
+		return nil, err
+	}
+	req.Passphrase = passphrase
+
 	return req, nil
 }
 
-func PrintGenerateKeyResponse(w io.Writer, resp *wallet.GenerateKeyResponse) {
-	walletHasBeenCreated := len(resp.Wallet.Mnemonic) != 0
-
+func PrintGenerateKeyResponse(w io.Writer, req *wallet.GenerateKeyRequest, resp *wallet.GenerateKeyResponse) {
 	p := printer.NewInteractivePrinter(w)
 
-	if walletHasBeenCreated {
-		p.CheckMark().Text("Wallet ").Bold(resp.Wallet.Name).Text(" has been created at: ").SuccessText(resp.Wallet.FilePath).NextLine()
-	}
-	p.CheckMark().Text("Key pair has been generated for wallet ").Bold(resp.Wallet.Name).Text(" at: ").SuccessText(resp.Wallet.FilePath).NextLine()
+	p.CheckMark().Text("Key pair has been generated in wallet ").Bold(req.Wallet).NextLine()
 	p.CheckMark().SuccessText("Generating a key pair succeeded").NextSection()
 
-	if walletHasBeenCreated {
-		p.Text("Wallet recovery phrase:").NextLine()
-		p.WarningText(resp.Wallet.Mnemonic).NextLine()
-		p.Text("Wallet version:").NextLine()
-		p.WarningText(fmt.Sprintf("%d", resp.Wallet.Version)).NextLine()
-	}
 	p.Text("Public key:").NextLine()
-	p.WarningText(resp.Key.PublicKey).NextLine()
+	p.WarningText(resp.PublicKey).NextLine()
 	p.Text("Metadata:").NextLine()
-	printMeta(p, resp.Key.Meta)
+	printMeta(p, resp.Meta)
 	p.NextSection()
 
-	if walletHasBeenCreated {
-		p.RedArrow().DangerText("Important").NextLine()
-		p.DangerText("Write down the ").DangerBold("recovery phrase").DangerText(" and the ").DangerBold("wallet's version").DangerText(", and store it somewhere safe and secure, now.").NextLine()
-		p.DangerText("The recovery phrase will not be displayed ever again, nor will you be able to retrieve it!").NextSection()
-		p.DangerText("Also, creating a wallet through this command is DEPRECATED. Please, use the `create` command.").NextSection()
-	}
-
 	p.BlueArrow().InfoText("Run the service").NextLine()
 	p.Text("Now, you can run the service. See the following command:").NextSection()
 	p.Code(fmt.Sprintf("%s service run --help", os.Args[0])).NextLine()
diff --git a/cmd/key_rotate.go b/cmd/key_rotate.go
index 1f118c1..736c462 100644
--- a/cmd/key_rotate.go
+++ b/cmd/key_rotate.go
@@ -160,7 +160,7 @@ func (f *RotateKeyFlags) Validate() (*wallet.RotateKeyRequest, error) {
 func PrintRotateKeyResponse(w io.Writer, req *wallet.RotateKeyResponse) {
 	p := printer.NewInteractivePrinter(w)
 	p.CheckMark().SuccessText("Key rotation succeeded").NextSection()
-	p.Text("Base64 encoded transaction:").NextLine()
+	p.Text("Transaction (base64-encoded):").NextLine()
 	p.Text(req.Base64Transaction).NextLine()
 	p.Text("Master public key used:").NextLine()
 	p.Text(req.MasterPublicKey).NextLine()
diff --git a/cmd/message.go b/cmd/message.go
new file mode 100644
index 0000000..c3eea0c
--- /dev/null
+++ b/cmd/message.go
@@ -0,0 +1,19 @@
+package cmd
+
+import (
+	"io"
+
+	"github.com/spf13/cobra"
+)
+
+func NewCmdMessage(w io.Writer, rf *RootFlags) *cobra.Command {
+	cmd := &cobra.Command{
+		Use:   "message",
+		Short: "Sign and verify messages",
+		Long:  "Sign and verify messages",
+	}
+
+	cmd.AddCommand(NewCmdSignMessage(w, rf))
+	cmd.AddCommand(NewCmdVerifyMessage(w, rf))
+	return cmd
+}
diff --git a/cmd/sign.go b/cmd/message_sign.go
similarity index 97%
rename from cmd/sign.go
rename to cmd/message_sign.go
index 7151d49..96da07a 100644
--- a/cmd/sign.go
+++ b/cmd/message_sign.go
@@ -21,7 +21,7 @@ var (
 
 	signMessageExample = cli.Examples(`
 		# Sign a message
-		vegawallet sign --message MESSAGE --wallet WALLET --pubkey PUBKEY
+		vegawallet message sign --message MESSAGE --wallet WALLET --pubkey PUBKEY
 	`)
 )
 
diff --git a/cmd/verify.go b/cmd/message_verify.go
similarity index 97%
rename from cmd/verify.go
rename to cmd/message_verify.go
index b5935d0..cd8c1ef 100644
--- a/cmd/verify.go
+++ b/cmd/message_verify.go
@@ -22,7 +22,7 @@ var (
 
 	verifyMessageExample = cli.Examples(`
 		# Verify the signature of a message
-		vegawallet verify --message MESSAGE --signature SIGNATURE --pubkey PUBKEY
+		vegawallet message verify --message MESSAGE --signature SIGNATURE --pubkey PUBKEY
 	`)
 )
 
diff --git a/cmd/network.go b/cmd/network.go
index d6e91fa..afc7795 100644
--- a/cmd/network.go
+++ b/cmd/network.go
@@ -16,6 +16,7 @@ func NewCmdNetwork(w io.Writer, rf *RootFlags) *cobra.Command {
 	cmd.AddCommand(NewCmdListNetworks(w, rf))
 	cmd.AddCommand(NewCmdImportNetwork(w, rf))
 	cmd.AddCommand(NewCmdDescribeNetwork(w, rf))
+	cmd.AddCommand(NewCmdLocateNetworks(w, rf))
 	cmd.AddCommand(NewCmdDeleteNetwork(w, rf))
 	return cmd
 }
diff --git a/cmd/network_delete.go b/cmd/network_delete.go
index daed057..339c1be 100644
--- a/cmd/network_delete.go
+++ b/cmd/network_delete.go
@@ -4,11 +4,11 @@ import (
 	"fmt"
 	"io"
 
+	vgterm "code.vegaprotocol.io/shared/libs/term"
 	"code.vegaprotocol.io/shared/paths"
 	"code.vegaprotocol.io/vegawallet/cmd/cli"
 	"code.vegaprotocol.io/vegawallet/cmd/flags"
 	"code.vegaprotocol.io/vegawallet/cmd/printer"
-	vgterm "code.vegaprotocol.io/vegawallet/libs/term"
 	"code.vegaprotocol.io/vegawallet/network"
 	netstore "code.vegaprotocol.io/vegawallet/network/store/v1"
 	"github.com/spf13/cobra"
diff --git a/cmd/network_locate.go b/cmd/network_locate.go
new file mode 100644
index 0000000..b49d244
--- /dev/null
+++ b/cmd/network_locate.go
@@ -0,0 +1,79 @@
+package cmd
+
+import (
+	"fmt"
+	"io"
+
+	"code.vegaprotocol.io/shared/paths"
+	"code.vegaprotocol.io/vegawallet/cmd/cli"
+	"code.vegaprotocol.io/vegawallet/cmd/flags"
+	"code.vegaprotocol.io/vegawallet/cmd/printer"
+	netstore "code.vegaprotocol.io/vegawallet/network/store/v1"
+	"github.com/spf13/cobra"
+)
+
+var (
+	locateNetworkLong = cli.LongDesc(`
+		Locate the folder in which all the network configuration files are stored.
+	`)
+
+	locateNetworkExample = cli.Examples(`
+		# Locate network configuration files
+		vegawallet network locate
+	`)
+)
+
+type LocateNetworksResponse struct {
+	Path string `json:"path"`
+}
+
+type LocateNetworksHandler func() (*LocateNetworksResponse, error)
+
+func NewCmdLocateNetworks(w io.Writer, rf *RootFlags) *cobra.Command {
+	h := func() (*LocateNetworksResponse, error) {
+		vegaPaths := paths.New(rf.Home)
+
+		netStore, err := netstore.InitialiseStore(vegaPaths)
+		if err != nil {
+			return nil, fmt.Errorf("couldn't initialise networks store: %w", err)
+		}
+
+		return &LocateNetworksResponse{
+			Path: netStore.GetNetworksPath(),
+		}, nil
+	}
+
+	return BuildCmdLocateNetworks(w, h, rf)
+}
+
+func BuildCmdLocateNetworks(w io.Writer, handler LocateNetworksHandler, rf *RootFlags) *cobra.Command {
+	cmd := &cobra.Command{
+		Use:     "locate",
+		Short:   "Locate the folder of network configuration files",
+		Long:    locateNetworkLong,
+		Example: locateNetworkExample,
+		RunE: func(_ *cobra.Command, _ []string) error {
+			resp, err := handler()
+			if err != nil {
+				return err
+			}
+
+			switch rf.Output {
+			case flags.InteractiveOutput:
+				PrintLocateNetworksResponse(w, resp)
+			case flags.JSONOutput:
+				return printer.FprintJSON(w, resp)
+			}
+
+			return nil
+		},
+	}
+
+	return cmd
+}
+
+func PrintLocateNetworksResponse(w io.Writer, resp *LocateNetworksResponse) {
+	p := printer.NewInteractivePrinter(w)
+
+	p.Text("Network configuration files are located at: ").SuccessText(resp.Path).NextLine()
+}
diff --git a/cmd/printer/interactive.go b/cmd/printer/interactive.go
index 563ec70..7851c88 100644
--- a/cmd/printer/interactive.go
+++ b/cmd/printer/interactive.go
@@ -3,6 +3,7 @@ package printer
 import (
 	"fmt"
 	"io"
+	"runtime"
 
 	"github.com/muesli/termenv"
 )
@@ -16,9 +17,7 @@ type InteractivePrinter struct {
 	arrow     termenv.Style
 }
 
-func NewInteractivePrinter(w io.Writer) *InteractivePrinter {
-	enableLegacyWindowsANSI()
-	profile := termenv.EnvColorProfile()
+func fancyPrinter(w io.Writer, profile termenv.Profile) *InteractivePrinter {
 	return &InteractivePrinter{
 		writer:    w,
 		profile:   profile,
@@ -29,6 +28,26 @@ func NewInteractivePrinter(w io.Writer) *InteractivePrinter {
 	}
 }
 
+func ansiPrinter(w io.Writer, profile termenv.Profile) *InteractivePrinter {
+	return &InteractivePrinter{
+		writer:    w,
+		profile:   profile,
+		checkMark: termenv.String("* ").Foreground(profile.Color("2")).String(),
+		bangMark:  termenv.String("! ").Foreground(profile.Color("1")).String(),
+		crossMark: termenv.String("x ").Foreground(profile.Color("1")).String(),
+		arrow:     termenv.String("> ").Foreground(profile.Color("2")),
+	}
+}
+
+func NewInteractivePrinter(w io.Writer) *InteractivePrinter {
+	enableLegacyWindowsANSI()
+	profile := termenv.EnvColorProfile()
+	if runtime.GOOS == "windows" {
+		return ansiPrinter(w, profile)
+	}
+	return fancyPrinter(w, profile)
+}
+
 func (p *InteractivePrinter) GreenArrow() *InteractivePrinter {
 	p.printOut(p.arrow.Foreground(p.profile.Color("2")).String())
 	return p
diff --git a/cmd/root.go b/cmd/root.go
index 4aa1b42..2db48ac 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -11,6 +11,8 @@ import (
 	"code.vegaprotocol.io/vegawallet/cmd/flags"
 	"code.vegaprotocol.io/vegawallet/cmd/printer"
 	"code.vegaprotocol.io/vegawallet/version"
+
+	vgversion "code.vegaprotocol.io/shared/libs/version"
 	"github.com/blang/semver/v4"
 	"github.com/spf13/cobra"
 )
@@ -39,7 +41,7 @@ func NewCmdRoot(w io.Writer) *cobra.Command {
 	vh := func() (*semver.Version, error) {
 		ctx, cancel := context.WithTimeout(context.Background(), requestTimeout)
 		defer cancel()
-		v, err := version.Check(version.BuildReleasesRequestFromGithub(ctx), version.Version)
+		v, err := version.Check(vgversion.BuildGithubReleasesRequestFrom(ctx, version.ReleasesAPI), version.Version)
 		if err != nil {
 			return nil, fmt.Errorf("couldn't check latest releases: %w", err)
 		}
@@ -87,7 +89,7 @@ func BuildCmdRoot(w io.Writer, vh CheckVersionHandler) *cobra.Command {
 
 				if v != nil {
 					p.Text("Version ").SuccessText(v.String()).Text(" is available. Your current version is ").DangerText(version.Version).Text(".").NextLine()
-					p.Text("Download the latest version at: ").Underline(version.GetReleaseURL(v)).NextSection()
+					p.Text("Download the latest version at: ").Underline(vgversion.GetGithubReleaseURL(version.ReleasesURL, v)).NextSection()
 				}
 			}
 			return nil
@@ -118,8 +120,6 @@ func BuildCmdRoot(w io.Writer, vh CheckVersionHandler) *cobra.Command {
 	// Root commands
 	cmd.AddCommand(NewCmdInit(w, f))
 	cmd.AddCommand(NewCmdCompletion(w))
-	cmd.AddCommand(NewCmdSignMessage(w, f))
-	cmd.AddCommand(NewCmdVerifyMessage(w, f))
 	cmd.AddCommand(NewCmdVersion(w, f))
 
 	// Sub-commands
@@ -128,6 +128,7 @@ func BuildCmdRoot(w io.Writer, vh CheckVersionHandler) *cobra.Command {
 	cmd.AddCommand(NewCmdNetwork(w, f))
 	cmd.AddCommand(NewCmdService(w, f))
 	cmd.AddCommand(NewCmdTx(w, f))
+	cmd.AddCommand(NewCmdMessage(w, f))
 
 	// Wallet commands
 	// We don't have a wrapper sub-command for wallet commands.
diff --git a/cmd/service_run.go b/cmd/service_run.go
index 9929c6b..e6f6742 100644
--- a/cmd/service_run.go
+++ b/cmd/service_run.go
@@ -11,11 +11,11 @@ import (
 	"syscall"
 	"text/template"
 
+	vglog "code.vegaprotocol.io/shared/libs/zap"
 	"code.vegaprotocol.io/shared/paths"
 	"code.vegaprotocol.io/vegawallet/cmd/cli"
 	"code.vegaprotocol.io/vegawallet/cmd/flags"
 	"code.vegaprotocol.io/vegawallet/cmd/printer"
-	vglog "code.vegaprotocol.io/vegawallet/libs/zap"
 	"code.vegaprotocol.io/vegawallet/network"
 	netstore "code.vegaprotocol.io/vegawallet/network/store/v1"
 	"code.vegaprotocol.io/vegawallet/node"
@@ -121,11 +121,6 @@ func BuildCmdRunService(w io.Writer, handler RunServiceHandler, rf *RootFlags) *
 		"",
 		"Network configuration to use",
 	)
-	cmd.Flags().BoolVar(&f.WithConsole,
-		"console-proxy",
-		false,
-		"Start the Vega console proxy and open the console in the default browser (DEPRECATED, use --with-console, instead)",
-	)
 	cmd.Flags().BoolVar(&f.WithConsole,
 		"with-console",
 		false,
@@ -207,6 +202,10 @@ func RunService(w io.Writer, rf *RootFlags, f *RunServiceFlags) error {
 		return fmt.Errorf("couldn't initialise network store: %w", err)
 	}
 
+	if err := verifyNetworkConfig(cfg, f); err != nil {
+		return err
+	}
+
 	logLevel := cfg.Level.String()
 	if len(f.LogLevel) != 0 {
 		logLevel = f.LogLevel
@@ -261,15 +260,36 @@ func RunService(w io.Writer, rf *RootFlags, f *RunServiceFlags) error {
 	} else if rf.Output == flags.JSONOutput {
 		log.Info(fmt.Sprintf("HTTP service started at: %s", serviceHost))
 	}
+	defer func() {
+		if err = srv.Stop(); err != nil {
+			log.Error("error while stopping HTTP server", zap.Error(err))
+		} else {
+			log.Info("HTTP server stopped with success")
+		}
+	}()
 
 	var cs *proxy.Proxy
 	if f.WithConsole {
 		cs = startConsole(log, rf, !f.NoBrowser, cfg, cancel, p)
+		defer func() {
+			if err = cs.Stop(); err != nil {
+				log.Error("error while stopping console proxy", zap.Error(err))
+			} else {
+				log.Info("console proxy stopped with success")
+			}
+		}()
 	}
 
 	var tokenDApp *proxy.Proxy
 	if f.WithTokenDApp {
 		tokenDApp = startTokenDApp(log, rf, !f.NoBrowser, cfg, cancel, p)
+		defer func() {
+			if err = tokenDApp.Stop(); err != nil {
+				log.Error("error while stopping token dApp proxy", zap.Error(err))
+			} else {
+				log.Info("token dApp proxy stopped with success")
+			}
+		}()
 	}
 
 	if rf.Output == flags.InteractiveOutput {
@@ -282,32 +302,23 @@ func RunService(w io.Writer, rf *RootFlags, f *RunServiceFlags) error {
 
 	waitSig(ctx, cancel, log)
 
-	if cs != nil {
-		if err = cs.Stop(); err != nil {
-			log.Error("error while stopping console proxy", zap.Error(err))
-		} else {
-			log.Info("console proxy stopped with success")
-		}
-	}
+	return nil
+}
 
-	if tokenDApp != nil {
-		if err = tokenDApp.Stop(); err != nil {
-			log.Error("error while stopping token dApp proxy", zap.Error(err))
-		} else {
-			log.Info("token dApp proxy stopped with success")
-		}
+func verifyNetworkConfig(cfg *network.Network, f *RunServiceFlags) error {
+	if err := cfg.EnsureCanConnectGRPCNode(); err != nil {
+		return err
 	}
-
-	if err = srv.Stop(); err != nil {
-		log.Error("error while stopping HTTP server", zap.Error(err))
-	} else {
-		log.Info("HTTP server stopped with success")
+	if f.WithConsole {
+		if err := cfg.EnsureCanConnectConsole(); err != nil {
+			return err
+		}
 	}
-
-	if rf.Output == flags.InteractiveOutput {
-		p.CheckMark().SuccessText("Service stopped").NextLine()
+	if f.WithTokenDApp {
+		if err := cfg.EnsureCanConnectTokenDApp(); err != nil {
+			return err
+		}
 	}
-
 	return nil
 }
 
diff --git a/cmd/tx.go b/cmd/tx.go
index b5c0312..b863f4b 100644
--- a/cmd/tx.go
+++ b/cmd/tx.go
@@ -31,8 +31,8 @@ func getHostsFromNetwork(rf *RootFlags, networkName string) ([]string, error) {
 		return nil, err
 	}
 
-	if len(net.API.GRPC.Hosts) == 0 {
-		return nil, ErrNetworkDoesNotHaveGRPCHostConfigured
+	if err := net.EnsureCanConnectGRPCNode(); err != nil {
+		return nil, err
 	}
 
 	return net.API.GRPC.Hosts, nil
diff --git a/cmd/tx_send.go b/cmd/tx_send.go
index 8f011e2..f802035 100644
--- a/cmd/tx_send.go
+++ b/cmd/tx_send.go
@@ -8,10 +8,10 @@ import (
 
 	api "code.vegaprotocol.io/protos/vega/api/v1"
 	commandspb "code.vegaprotocol.io/protos/vega/commands/v1"
+	vglog "code.vegaprotocol.io/shared/libs/zap"
 	"code.vegaprotocol.io/vegawallet/cmd/cli"
 	"code.vegaprotocol.io/vegawallet/cmd/flags"
 	"code.vegaprotocol.io/vegawallet/cmd/printer"
-	vglog "code.vegaprotocol.io/vegawallet/libs/zap"
 	"code.vegaprotocol.io/vegawallet/network"
 	"code.vegaprotocol.io/vegawallet/node"
 	"github.com/golang/protobuf/proto"
diff --git a/cmd/version.go b/cmd/version.go
index 74ae20d..1dd0480 100644
--- a/cmd/version.go
+++ b/cmd/version.go
@@ -1,7 +1,9 @@
 package cmd
 
 import (
+	"fmt"
 	"io"
+	"os"
 
 	"code.vegaprotocol.io/vegawallet/cmd/cli"
 	"code.vegaprotocol.io/vegawallet/cmd/flags"
@@ -57,6 +59,11 @@ func BuildCmdGetVersion(w io.Writer, handler GetVersionHandler, rf *RootFlags) *
 func PrintGetVersionResponse(w io.Writer, resp *version.GetVersionResponse) {
 	p := printer.NewInteractivePrinter(w)
 
-	p.Text("Version:").NextLine().WarningText(resp.Version).NextSection()
+	p.Text("Software version:").NextLine().WarningText(resp.Version).NextSection()
 	p.Text("Git hash:").NextLine().WarningText(resp.GitHash).NextSection()
+
+	p.RedArrow().DangerText("Important").NextLine()
+	p.Text("This command does NOT give you your wallet version.").NextLine()
+	p.Text("To get this information, see the following command:").NextSection()
+	p.Code(fmt.Sprintf("%s info --help", os.Args[0])).NextLine()
 }
diff --git a/cmd/wallet_delete.go b/cmd/wallet_delete.go
index bb0c7b3..9f32f2e 100644
--- a/cmd/wallet_delete.go
+++ b/cmd/wallet_delete.go
@@ -5,10 +5,10 @@ import (
 	"fmt"
 	"io"
 
+	vgterm "code.vegaprotocol.io/shared/libs/term"
 	"code.vegaprotocol.io/vegawallet/cmd/cli"
 	"code.vegaprotocol.io/vegawallet/cmd/flags"
 	"code.vegaprotocol.io/vegawallet/cmd/printer"
-	vgterm "code.vegaprotocol.io/vegawallet/libs/term"
 	"code.vegaprotocol.io/vegawallet/wallets"
 	"github.com/spf13/cobra"
 )
diff --git a/cmd/wallet_import.go b/cmd/wallet_import.go
index 0652da5..9d193ce 100644
--- a/cmd/wallet_import.go
+++ b/cmd/wallet_import.go
@@ -90,11 +90,6 @@ func BuildCmdImportWallet(w io.Writer, handler ImportWalletHandler, rf *RootFlag
 		"",
 		"Path to the file containing the passphrase to access the wallet",
 	)
-	cmd.Flags().StringVarP(&f.RecoveryPhraseFile,
-		"mnemonic-file", "m",
-		"",
-		"Path to the file containing the recovery phrase of the wallet (DEPRECATED, use --recovery-phrase-file instead)",
-	)
 	cmd.Flags().StringVar(&f.RecoveryPhraseFile,
 		"recovery-phrase-file",
 		"",
diff --git a/commands/commands.go b/commands/commands.go
index a86e82d..304ac06 100644
--- a/commands/commands.go
+++ b/commands/commands.go
@@ -51,6 +51,10 @@ func CheckSubmitTransactionRequest(req *walletpb.SubmitTransactionRequest) comma
 		cmdErr = commands.CheckUndelegateSubmission(cmd.UndelegateSubmission)
 	case *walletpb.SubmitTransactionRequest_DelegateSubmission:
 		cmdErr = commands.CheckDelegateSubmission(cmd.DelegateSubmission)
+	case *walletpb.SubmitTransactionRequest_LiquidityProvisionCancellation:
+		cmdErr = commands.CheckLiquidityProvisionCancellation(cmd.LiquidityProvisionCancellation)
+	case *walletpb.SubmitTransactionRequest_LiquidityProvisionAmendment:
+		cmdErr = commands.CheckLiquidityProvisionAmendment(cmd.LiquidityProvisionAmendment)
 	default:
 		errs.AddForProperty("input_data.command", commands.ErrIsNotSupported)
 	}
@@ -126,6 +130,14 @@ func wrapRequestCommandIntoInputData(data *commandspb.InputData, req *walletpb.S
 		data.Command = &commandspb.InputData_UndelegateSubmission{
 			UndelegateSubmission: req.GetUndelegateSubmission(),
 		}
+	case *walletpb.SubmitTransactionRequest_LiquidityProvisionCancellation:
+		data.Command = &commandspb.InputData_LiquidityProvisionCancellation{
+			LiquidityProvisionCancellation: req.GetLiquidityProvisionCancellation(),
+		}
+	case *walletpb.SubmitTransactionRequest_LiquidityProvisionAmendment:
+		data.Command = &commandspb.InputData_LiquidityProvisionAmendment{
+			LiquidityProvisionAmendment: req.GetLiquidityProvisionAmendment(),
+		}
 	default:
 		panic(fmt.Sprintf("command %v is not supported", cmd))
 	}
diff --git a/go.mod b/go.mod
index 8b94c04..1672455 100644
--- a/go.mod
+++ b/go.mod
@@ -3,15 +3,14 @@ module code.vegaprotocol.io/vegawallet
 go 1.17
 
 require (
-	code.vegaprotocol.io/protos v0.47.0
-	code.vegaprotocol.io/shared v0.0.0-20211015074835-9ed837d93090
+	code.vegaprotocol.io/protos v0.48.0
+	code.vegaprotocol.io/shared v0.0.0-20220202150846-b6aba31dcdb0
 	github.com/blang/semver/v4 v4.0.0
 	github.com/cenkalti/backoff/v4 v4.0.2
 	github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1
 	github.com/golang/mock v1.4.3
 	github.com/golang/protobuf v1.4.3
 	github.com/julienschmidt/httprouter v1.3.0
-	github.com/mattn/go-isatty v0.0.13
 	github.com/muesli/termenv v0.9.0
 	github.com/oasisprotocol/curve25519-voi v0.0.0-20210716083614-f38f8e8b0b84
 	github.com/rs/cors v1.7.0
@@ -20,38 +19,30 @@ require (
 	github.com/stretchr/testify v1.7.0
 	github.com/tyler-smith/go-bip39 v1.1.0
 	github.com/vegaprotocol/go-slip10 v0.1.0
-	go.uber.org/zap v1.13.0
-	golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1
+	go.uber.org/zap v1.20.0
+	golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c
 	golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b
 	google.golang.org/grpc v1.38.0
 )
 
 require (
-	github.com/BurntSushi/toml v0.3.1 // indirect
+	github.com/BurntSushi/toml v1.0.0 // indirect
 	github.com/adrg/xdg v0.3.3 // indirect
 	github.com/davecgh/go-spew v1.1.1 // indirect
-	github.com/gogo/protobuf v1.3.2 // indirect
 	github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
 	github.com/inconshreveable/mousetrap v1.0.0 // indirect
 	github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
+	github.com/mattn/go-isatty v0.0.14 // indirect
 	github.com/mattn/go-runewidth v0.0.13 // indirect
-	github.com/mwitkow/go-proto-validators v0.1.0 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
 	github.com/rivo/uniseg v0.2.0 // indirect
 	github.com/spf13/pflag v1.0.5 // indirect
-	github.com/zannen/toml v0.3.2 // indirect
-	go.uber.org/atomic v1.5.0 // indirect
-	go.uber.org/multierr v1.3.0 // indirect
-	go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee // indirect
+	go.uber.org/atomic v1.7.0 // indirect
+	go.uber.org/multierr v1.6.0 // indirect
 	golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect
-	golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect
-	golang.org/x/mod v0.4.2 // indirect
 	golang.org/x/net v0.0.0-20210610132358-84b48f89b13b // indirect
 	golang.org/x/text v0.3.6 // indirect
-	golang.org/x/tools v0.1.2 // indirect
-	golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
 	google.golang.org/genproto v0.0.0-20201119123407-9b1e624d6bc4 // indirect
 	google.golang.org/protobuf v1.26.0-rc.1 // indirect
-	gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
-	honnef.co/go/tools v0.0.1-2019.2.3 // indirect
+	gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
 )
diff --git a/go.sum b/go.sum
index eaccc17..136a9e2 100644
--- a/go.sum
+++ b/go.sum
@@ -10,15 +10,20 @@ cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7
 cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
 cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
 cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
-code.vegaprotocol.io/protos v0.46.1-0.20211201150331-b536716fea70 h1:zqWTK+nnd1HK7oCCXIK/H3uoyync4R1flKX+A5XKsmQ=
-code.vegaprotocol.io/protos v0.46.1-0.20211201150331-b536716fea70/go.mod h1:NORK22Evpwn0GoetL7H6/oVUzGcfjAJEJTzw+zTkSyE=
-code.vegaprotocol.io/protos v0.47.0 h1:gFx/dc9gwwcVmEHca481fhJEkSabhfZTuOfmNUZ+7hs=
-code.vegaprotocol.io/protos v0.47.0/go.mod h1:NORK22Evpwn0GoetL7H6/oVUzGcfjAJEJTzw+zTkSyE=
-code.vegaprotocol.io/shared v0.0.0-20211015074835-9ed837d93090 h1:SF7ZRvZ/JASJNEQigbuUwk1rQuugLy6gqbOFs8D8VXI=
-code.vegaprotocol.io/shared v0.0.0-20211015074835-9ed837d93090/go.mod h1:dmahiiphuDJvjXXDvQvo22tFPx7yk9wW8z1glU/JgHE=
+code.vegaprotocol.io/protos v0.47.1-0.20220128102417-182c89e00442 h1:0jBV7Ltp2SpUzmO1u1JStgVoF1vbuBvIkd7pVg9cqdM=
+code.vegaprotocol.io/protos v0.47.1-0.20220128102417-182c89e00442/go.mod h1:KqJcg62sUHV0qEhlZIZojEypzk3V1ZE3tBS+wdqjddU=
+code.vegaprotocol.io/protos v0.48.0 h1:5yfLnh7xZrkQWiNOdYQI3GqJIs12G1Gjy/Uf3uyvDao=
+code.vegaprotocol.io/protos v0.48.0/go.mod h1:KqJcg62sUHV0qEhlZIZojEypzk3V1ZE3tBS+wdqjddU=
+code.vegaprotocol.io/shared v0.0.0-20220128163854-7eab67fa60d2 h1:GKg+68ulAI0UPhru2FgGE+qGuqoTQbnNDF31LU8e+jA=
+code.vegaprotocol.io/shared v0.0.0-20220128163854-7eab67fa60d2/go.mod h1:pNHKwqRDkotUN0s6jErl7Eb2EpZa2HAf03lyPMTCUT0=
+code.vegaprotocol.io/shared v0.0.0-20220202144944-ab85fdc4d546 h1:R2f7lxYmQ7mkc7L6wcNpe0gN/zBsCDRERK3yMPjFFfM=
+code.vegaprotocol.io/shared v0.0.0-20220202144944-ab85fdc4d546/go.mod h1:pNHKwqRDkotUN0s6jErl7Eb2EpZa2HAf03lyPMTCUT0=
+code.vegaprotocol.io/shared v0.0.0-20220202150846-b6aba31dcdb0 h1:vDjKTd508FwIwoSdZBEvtEfmBeaYdodgLM96v253zKI=
+code.vegaprotocol.io/shared v0.0.0-20220202150846-b6aba31dcdb0/go.mod h1:pNHKwqRDkotUN0s6jErl7Eb2EpZa2HAf03lyPMTCUT0=
 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
-github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/BurntSushi/toml v1.0.0 h1:dtDWrepsVPfW9H/4y7dDgFc2MBUSeJhlaDtK13CxFlU=
+github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
 github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d/go.mod h1:URdX5+vg25ts3aCh8H5IFZybJYKWhJHYMTnf+ULtoC4=
 github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
@@ -48,6 +53,8 @@ github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6l
 github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
 github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
 github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
+github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
+github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
 github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
 github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
@@ -154,7 +161,6 @@ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a
 github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
 github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
-github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
 github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -287,8 +293,9 @@ github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czP
 github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
 github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
 github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
-github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA=
 github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
+github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
+github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
 github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
 github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
 github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
@@ -312,8 +319,6 @@ github.com/muesli/termenv v0.9.0 h1:wnbOaGz+LUR3jNT0zOzinPnyDaCZUQRZj9GxK8eRVl8=
 github.com/muesli/termenv v0.9.0/go.mod h1:R/LzAKf+suGs4IsO95y7+7DpFHO0KABgnZqtlyx2mBw=
 github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
-github.com/mwitkow/go-proto-validators v0.1.0 h1:2Org0/cGKUUUDzoLSRSsGJDqyLWrb5lG57o5+QdRr8M=
-github.com/mwitkow/go-proto-validators v0.1.0/go.mod h1:bA3eoTMLQkf/A7h7JOC3ddMBLXwS19KK7DEeSPL1O+4=
 github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
 github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
 github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k=
@@ -473,8 +478,6 @@ github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:
 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
-github.com/zannen/toml v0.3.2 h1:IYPp4GVtabUyQpzSU6xP1VDyKR3Ico3qhNP2VxCkl2Y=
-github.com/zannen/toml v0.3.2/go.mod h1:17qwsU2a1aZpHAu+nMR+zAzGupaxRIaCvxBhvCp5YGk=
 go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
 go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
 go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
@@ -486,16 +489,20 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
 go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
 go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
 go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
-go.uber.org/atomic v1.5.0 h1:OI5t8sDa1Or+q8AeE+yKeB/SDYioSHAgcVljj9JIETY=
 go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
+go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
+go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
+go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
+go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
 go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
-go.uber.org/multierr v1.3.0 h1:sFPn2GLc3poCkfrpIXGhBD2X0CMIo4Q/zSULXrj/+uc=
 go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
-go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
+go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
+go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
 go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
 go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
-go.uber.org/zap v1.13.0 h1:nR6NoDBgAf67s68NhaXbsojM+2gxp3S1hWkHDl27pVU=
 go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
+go.uber.org/zap v1.20.0 h1:N4oPlghZwYG55MlU6LXk/Zp00FVNE9X9wrYO8CEs4lc=
+go.uber.org/zap v1.20.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
 golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@@ -530,8 +537,6 @@ golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHl
 golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
 golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
 golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug=
-golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
 golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
 golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
 golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
@@ -540,7 +545,6 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
 golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
 golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
 golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -628,8 +632,9 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4=
 golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
+golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE=
@@ -669,12 +674,10 @@ golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtn
 golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
 golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
 golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
 golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.1.2 h1:kRBLX7v7Af8W7Gdbbc908OJcdgtK8bOz9Uaj8/F1ACA=
-golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -765,15 +768,16 @@ gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
 gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
+gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
 honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
 rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
 rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
diff --git a/libs/term/tty.go b/libs/term/tty.go
deleted file mode 100644
index 134c475..0000000
--- a/libs/term/tty.go
+++ /dev/null
@@ -1,15 +0,0 @@
-package term
-
-import (
-	"os"
-
-	"github.com/mattn/go-isatty"
-)
-
-func HasTTY() bool {
-	return isatty.IsTerminal(os.Stdout.Fd()) || isatty.IsCygwinTerminal(os.Stdout.Fd())
-}
-
-func HasNoTTY() bool {
-	return !HasTTY()
-}
diff --git a/libs/zap/zap.go b/libs/zap/zap.go
deleted file mode 100644
index c9eb64c..0000000
--- a/libs/zap/zap.go
+++ /dev/null
@@ -1,17 +0,0 @@
-package zap
-
-import (
-	"fmt"
-
-	"go.uber.org/zap"
-)
-
-func Sync(logger *zap.Logger) func() {
-	return func() {
-		err := logger.Sync()
-		if err != nil {
-			// This is the ultimate warning, as we can't do anything else.
-			fmt.Printf("couldn't flush logger: %v", err) //nolint:forbidigo
-		}
-	}
-}
diff --git a/makefile b/makefile
deleted file mode 100644
index 16e7302..0000000
--- a/makefile
+++ /dev/null
@@ -1,41 +0,0 @@
-ifeq ($(CI),)
-	# Not in CI
-	VERSION := dev-$(USER)
-	VERSION_HASH := $(shell git rev-parse HEAD | cut -b1-8)
-else
-	# In CI
-	ifneq ($(RELEASE_VERSION),)
-		VERSION := $(RELEASE_VERSION)
-	else
-		# No tag, so make one
-		VERSION := $(shell git describe --tags 2>/dev/null)
-	endif
-	VERSION_HASH := $(shell echo "$(GITHUB_SHA)" | cut -b1-8)
-endif
-
-install:
-	go install -v -ldflags "-X code.vegaprotocol.io/vegawallet/version.Version=${VERSION} -X code.vegaprotocol.io/vegawallet/version.VersionHash=${VERSION_HASH}"
-
-release-windows-amd64:
-	GOOS=windows GOARCH=amd64 CGO_ENABLED=0 go build -o build/vegawallet.exe -ldflags "-X code.vegaprotocol.io/vegawallet/version.Version=${VERSION} -X code.vegaprotocol.io/vegawallet/version.VersionHash=${VERSION_HASH}"
-	cd build && 7z a -tzip vegawallet-windows-amd64.zip vegawallet.exe
-
-release-windows-arm64:
-	GOOS=windows GOARCH=arm64 CGO_ENABLED=0 go build -o build/vegawallet.exe -ldflags "-X code.vegaprotocol.io/vegawallet/version.Version=${VERSION} -X code.vegaprotocol.io/vegawallet/version.VersionHash=${VERSION_HASH}"
-	cd build && 7z a -tzip vegawallet-windows-arm64.zip vegawallet.exe
-
-release-macos-amd64:
-	GOOS=darwin GOARCH=amd64 CGO_ENABLED=0 go build -o build/vegawallet -ldflags "-X code.vegaprotocol.io/vegawallet/version.Version=${VERSION} -X code.vegaprotocol.io/vegawallet/version.VersionHash=${VERSION_HASH}"
-	cd build && zip vegawallet-darwin-amd64.zip vegawallet
-
-release-macos-arm64:
-	GOOS=darwin GOARCH=arm64 CGO_ENABLED=0 go build -o build/vegawallet -ldflags "-X code.vegaprotocol.io/vegawallet/version.Version=${VERSION} -X code.vegaprotocol.io/vegawallet/version.VersionHash=${VERSION_HASH}"
-	cd build && zip vegawallet-darwin-arm64.zip vegawallet
-
-release-linux-amd64:
-	GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o build/vegawallet -ldflags "-X code.vegaprotocol.io/vegawallet/version.Version=${VERSION} -X code.vegaprotocol.io/vegawallet/version.VersionHash=${VERSION_HASH}"
-	cd build && zip vegawallet-linux-amd64.zip vegawallet
-
-release-linux-arm64:
-	GOOS=linux GOARCH=arm64 CGO_ENABLED=0 go build -o build/vegawallet -ldflags "-X code.vegaprotocol.io/vegawallet/version.Version=${VERSION} -X code.vegaprotocol.io/vegawallet/version.VersionHash=${VERSION_HASH}"
-	cd build && zip vegawallet-linux-arm64.zip vegawallet
diff --git a/network/network.go b/network/network.go
index e81f80a..32347e2 100644
--- a/network/network.go
+++ b/network/network.go
@@ -1,6 +1,18 @@
 package network
 
-import "code.vegaprotocol.io/vegawallet/service/encoding"
+import (
+	"errors"
+
+	"code.vegaprotocol.io/vegawallet/service/encoding"
+)
+
+var (
+	ErrNetworkDoesNotHaveGRPCHostConfigured              = errors.New("network configuration does not have any gRPC host set")
+	ErrNetworkDoesNotHaveHostConfiguredForConsole        = errors.New("network configuration does not have any host set for console")
+	ErrNetworkDoesNotHaveLocalPortConfiguredForConsole   = errors.New("network configuration does not have any local port set for console")
+	ErrNetworkDoesNotHaveHostConfiguredForTokenDApp      = errors.New("network configuration does not have any host set for token dApp")
+	ErrNetworkDoesNotHaveLocalPortConfiguredForTokenDApp = errors.New("network configuration does not have any local port set for token dApp")
+)
 
 type Network struct {
 	Name        string
@@ -11,9 +23,6 @@ type Network struct {
 	API         APIConfig
 	TokenDApp   TokenDAppConfig
 	Console     ConsoleConfig
-
-	// TO REMOVE Once the tools use the new API.GRPC
-	Nodes GRPCConfig
 }
 
 type APIConfig struct {
@@ -44,3 +53,30 @@ type TokenDAppConfig struct {
 	URL       string
 	LocalPort int
 }
+
+func (n *Network) EnsureCanConnectGRPCNode() error {
+	if len(n.API.GRPC.Hosts) > 0 && len(n.API.GRPC.Hosts[0]) > 0 {
+		return nil
+	}
+	return ErrNetworkDoesNotHaveGRPCHostConfigured
+}
+
+func (n *Network) EnsureCanConnectConsole() error {
+	if len(n.Console.URL) == 0 {
+		return ErrNetworkDoesNotHaveHostConfiguredForConsole
+	}
+	if n.Console.LocalPort == 0 {
+		return ErrNetworkDoesNotHaveLocalPortConfiguredForConsole
+	}
+	return nil
+}
+
+func (n *Network) EnsureCanConnectTokenDApp() error {
+	if len(n.TokenDApp.URL) == 0 {
+		return ErrNetworkDoesNotHaveHostConfiguredForTokenDApp
+	}
+	if n.TokenDApp.LocalPort == 0 {
+		return ErrNetworkDoesNotHaveLocalPortConfiguredForTokenDApp
+	}
+	return nil
+}
diff --git a/network/store/v1/store.go b/network/store/v1/store.go
index ce326d3..563c1ff 100644
--- a/network/store/v1/store.go
+++ b/network/store/v1/store.go
@@ -64,12 +64,10 @@ func (s *Store) GetNetwork(name string) (*network.Network, error) {
 	if name != net.Name {
 		return nil, NewDifferentNetworkNamesError(name, net.Name)
 	}
-	migrateNetwork(net)
 	return net, nil
 }
 
 func (s *Store) SaveNetwork(net *network.Network) error {
-	migrateNetwork(net)
 	if err := paths.WriteStructuredFile(s.nameToFilePath(net.Name), net); err != nil {
 		return fmt.Errorf("couldn't write network configuration file: %w", err)
 	}
@@ -88,14 +86,3 @@ func (s *Store) nameToFilePath(network string) string {
 func (s *Store) fileNameToName(fileName string) string {
 	return fileName[:len(fileName)-len(fileExt)]
 }
-
-// migrateNetwork ensures the legacy configuration is migrated to the new
-// one.
-// TO REMOVE Once the tools use the new API.GRPC.
-func migrateNetwork(net *network.Network) {
-	if len(net.Nodes.Hosts) > 0 && len(net.API.GRPC.Hosts) == 0 {
-		net.API.GRPC = net.Nodes
-	}
-	// void this legacy property to avoid confusion
-	net.Nodes = network.GRPCConfig{}
-}
diff --git a/service/service.go b/service/service.go
index b0eb2e4..ab4dc35 100644
--- a/service/service.go
+++ b/service/service.go
@@ -71,16 +71,13 @@ func ParseCreateWalletRequest(r *http.Request) (*CreateWalletRequest, commands.E
 // recovery phrase of the created wallet.
 type CreateWalletResponse struct {
 	RecoveryPhrase string `json:"recoveryPhrase"`
-	// Deprecated: Use recoveryPhrase instead.
-	Mnemonic string `json:"mnemonic"`
-	Token    string `json:"token"`
+	Token          string `json:"token"`
 }
 
 // ImportWalletRequest describes the request for ImportWallet.
 type ImportWalletRequest struct {
 	Wallet         string `json:"wallet"`
 	Passphrase     string `json:"passphrase"`
-	Mnemonic       string `json:"mnemonic"`
 	RecoveryPhrase string `json:"recoveryPhrase"`
 	Version        uint32 `json:"version"`
 }
@@ -101,10 +98,6 @@ func ParseImportWalletRequest(r *http.Request) (*ImportWalletRequest, commands.E
 		errs.AddForProperty("passphrase", commands.ErrIsRequired)
 	}
 
-	// Support for deprecated mnemonic property.
-	if len(req.RecoveryPhrase) == 0 {
-		req.RecoveryPhrase = req.Mnemonic
-	}
 	if len(req.RecoveryPhrase) == 0 {
 		errs.AddForProperty("recoveryPhrase", commands.ErrIsRequired)
 	}
@@ -486,7 +479,6 @@ func (s *Service) CreateWallet(w http.ResponseWriter, r *http.Request, _ httprou
 
 	s.writeSuccess(w, CreateWalletResponse{
 		RecoveryPhrase: recoveryPhrase,
-		Mnemonic:       recoveryPhrase,
 		Token:          token,
 	})
 }
diff --git a/version/adapter.go b/version/adapter.go
deleted file mode 100644
index 85bca16..0000000
--- a/version/adapter.go
+++ /dev/null
@@ -1,60 +0,0 @@
-package version
-
-import (
-	"context"
-	"encoding/json"
-	"fmt"
-	"io"
-	"net/http"
-)
-
-const ReleaseAPI = "https://api.github.com/repos/vegaprotocol/vegawallet/releases"
-
-type ReleasesGetter func() ([]string, error)
-
-type releaseResponse struct {
-	Name string `json:"name"`
-}
-
-func BuildReleasesRequestFromGithub(ctx context.Context) ReleasesGetter {
-	return func() ([]string, error) {
-		req, err := http.NewRequestWithContext(ctx, http.MethodGet, ReleaseAPI, nil)
-		if err != nil {
-			return nil, fmt.Errorf("couldn't build request: %w", err)
-		}
-		req.Header.Add("Accept", "application/vnd.github.v3+json")
-
-		client := &http.Client{}
-		resp, err := client.Do(req)
-		if err != nil {
-			return nil, fmt.Errorf("couldn't deliver request: %w", err)
-		}
-		defer resp.Body.Close()
-
-		body, err := io.ReadAll(resp.Body)
-		if err != nil {
-			return nil, fmt.Errorf("couldn't read response body: %w", err)
-		}
-
-		responses := []releaseResponse{}
-		if err = json.Unmarshal(body, &responses); err != nil {
-			// try to parse as a general error message which would be useful information
-			// to know eg. if we were blocked due to githubs rate-limiting
-			m := struct {
-				Message string `json:"message"`
-			}{}
-			if mErr := json.Unmarshal(body, &m); mErr == nil {
-				return nil, fmt.Errorf("couldn't read response message: %s: %w", m.Message, err)
-			}
-
-			return nil, fmt.Errorf("couldn't unmarshal response body: %w", err)
-		}
-
-		releases := make([]string, 0, len(responses))
-		for _, response := range responses {
-			releases = append(releases, response.Name)
-		}
-
-		return releases, nil
-	}
-}
diff --git a/version/version.go b/version/version.go
index 6c74dfc..c94a9e4 100644
--- a/version/version.go
+++ b/version/version.go
@@ -1,16 +1,16 @@
 package version
 
 import (
-	"fmt"
-	"strings"
+	vgversion "code.vegaprotocol.io/shared/libs/version"
 
 	"github.com/blang/semver/v4"
 )
 
 const (
-	TaggedReleaseURL   = "https://github.com/vegaprotocol/vegawallet/releases/tag"
+	ReleasesAPI        = "https://api.github.com/repos/vegaprotocol/vegawallet/releases"
+	ReleasesURL        = "https://github.com/vegaprotocol/vegawallet/releases"
 	defaultVersionHash = "unknown"
-	defaultVersion     = "v0.11.0"
+	defaultVersion     = "v0.12.0"
 )
 
 var (
@@ -24,13 +24,7 @@ var (
 )
 
 func IsUnreleased() bool {
-	v, err := extractVersionFromRelease(Version)
-	if err != nil {
-		// unsupported version, considered unreleased
-		return true
-	}
-
-	return v.isDevelopmentVersion
+	return vgversion.IsUnreleased(Version)
 }
 
 type GetVersionResponse struct {
@@ -45,92 +39,6 @@ func GetVersionInfo() *GetVersionResponse {
 	}
 }
 
-// Check returns a newer version, or an error or nil for both
-// if no error happened, and no updates are needed.
-func Check(releasesGetterFn ReleasesGetter, currentRelease string) (*semver.Version, error) {
-	releases, err := releasesGetterFn()
-	if err != nil {
-		return nil, fmt.Errorf("couldn't get releases: %w", err)
-	}
-
-	currentVersion, err := extractVersionFromRelease(currentRelease)
-	if err != nil {
-		return nil, fmt.Errorf("couldn't extract version from release: %w", err)
-	}
-	latestVersion := currentVersion
-
-	var updateAvailable bool
-	for _, release := range releases {
-		comparedVersion, err := extractVersionFromRelease(release)
-		if err != nil {
-			// unsupported version
-			continue
-		}
-
-		if shouldUpdate(latestVersion, comparedVersion) {
-			updateAvailable = true
-			latestVersion = comparedVersion
-		}
-	}
-
-	if !updateAvailable {
-		return nil, nil
-	}
-
-	return latestVersion.version, nil
-}
-
-func GetReleaseURL(v *semver.Version) string {
-	return fmt.Sprintf("%v/v%v", TaggedReleaseURL, v)
-}
-
-func shouldUpdate(latestVersion *cachedVersion, comparedVersion *cachedVersion) bool {
-	if latestVersion.isStable && !comparedVersion.isStable {
-		return false
-	}
-
-	if latestVersion.isDevelopmentVersion && nonDevelopmentVersionAvailable(latestVersion, comparedVersion) {
-		return true
-	}
-
-	return comparedVersion.version.GT(*latestVersion.version)
-}
-
-// nonDevelopmentVersionAvailable verifies if the compared version is the
-// non-development equivalent of the latest version.
-// For example, 0.9.0-pre1 is the non-development version of 0.9.0-pre1+dev.
-// In semantic versioning, we don't compare the `build` annotation, so verifying
-// equality is safe.
-func nonDevelopmentVersionAvailable(latestVersion *cachedVersion, comparedVersion *cachedVersion) bool {
-	return comparedVersion.version.EQ(*latestVersion.version)
-}
-
-func extractVersionFromRelease(release string) (*cachedVersion, error) {
-	version, err := semver.New(strings.TrimPrefix(release, "v"))
-	return asCachedVersion(version), err
-}
-
-type cachedVersion struct {
-	// version is the original version
-	version *semver.Version
-	// isDevelopmentVersion tells if the version has a `dev` build annotation.
-	isDevelopmentVersion bool
-	// isStable tells if the version has any pre-release annotations.
-	isStable bool
-}
-
-func asCachedVersion(v *semver.Version) *cachedVersion {
-	lv := &cachedVersion{
-		version: v,
-	}
-
-	for _, build := range v.Build {
-		if build == "dev" {
-			lv.isDevelopmentVersion = true
-		}
-	}
-
-	lv.isStable = !lv.isDevelopmentVersion && len(v.Pre) == 0
-
-	return lv
+func Check(releasesGetterFn vgversion.ReleasesGetter, currentRelease string) (*semver.Version, error) {
+	return vgversion.Check(releasesGetterFn, currentRelease)
 }
diff --git a/wallet/handler.go b/wallet/handler.go
index b67f9b3..a7b815a 100644
--- a/wallet/handler.go
+++ b/wallet/handler.go
@@ -7,6 +7,8 @@ import (
 
 	"code.vegaprotocol.io/protos/commands"
 	commandspb "code.vegaprotocol.io/protos/vega/commands/v1"
+	walletpb "code.vegaprotocol.io/protos/vega/wallet/v1"
+	wcommands "code.vegaprotocol.io/vegawallet/commands"
 	"github.com/golang/protobuf/proto"
 )
 
@@ -26,67 +28,37 @@ type GenerateKeyRequest struct {
 }
 
 type GenerateKeyResponse struct {
-	// Deprecated: Wallet won't be generated by key generate anymore, so no need
-	// to return it.
-	Wallet struct {
-		Name     string `json:"name"`
-		Version  uint32 `json:"version"`
-		FilePath string `json:"filePath"`
-		Mnemonic string `json:"mnemonic,omitempty"`
-	} `json:"wallet"`
-	Key struct {
-		PublicKey string `json:"publicKey"`
-		Algorithm struct {
-			Name    string `json:"name"`
-			Version uint32 `json:"version"`
-		} `json:"algorithm"`
-		Meta []Meta `json:"meta"`
-	} `json:"key"`
+	PublicKey string `json:"publicKey"`
+	Algorithm struct {
+		Name    string `json:"name"`
+		Version uint32 `json:"version"`
+	} `json:"algorithm"`
+	Meta []Meta `json:"meta"`
 }
 
 func GenerateKey(store Store, req *GenerateKeyRequest) (*GenerateKeyResponse, error) {
 	resp := &GenerateKeyResponse{}
 
-	walletExists := store.WalletExists(req.Wallet)
-
-	var wal Wallet
-	if !walletExists {
-		w, recoveryPhrase, err := NewHDWallet(req.Wallet)
-		if err != nil {
-			return nil, fmt.Errorf("couldn't create HD wallet: %w", err)
-		}
-		wal = w
-
-		resp.Wallet.Mnemonic = recoveryPhrase
-	} else {
-		w, err := store.GetWallet(req.Wallet, req.Passphrase)
-		if err != nil {
-			if errors.Is(err, ErrWrongPassphrase) {
-				return nil, err
-			}
-			return nil, fmt.Errorf("couldn't get wallet %s: %w", req.Wallet, err)
-		}
-		wal = w
+	w, err := getWallet(store, req.Wallet, req.Passphrase)
+	if err != nil {
+		return nil, err
 	}
 
-	req.Metadata = addDefaultKeyName(wal, req.Metadata)
+	req.Metadata = addDefaultKeyName(w, req.Metadata)
 
-	kp, err := wal.GenerateKeyPair(req.Metadata)
+	kp, err := w.GenerateKeyPair(req.Metadata)
 	if err != nil {
 		return nil, err
 	}
 
-	if err := store.SaveWallet(wal, req.Passphrase); err != nil {
+	if err := store.SaveWallet(w, req.Passphrase); err != nil {
 		return nil, fmt.Errorf("couldn't save wallet: %w", err)
 	}
 
-	resp.Wallet.Name = req.Wallet
-	resp.Wallet.Version = wal.Version()
-	resp.Wallet.FilePath = store.GetWalletPath(req.Wallet)
-	resp.Key.PublicKey = kp.PublicKey()
-	resp.Key.Algorithm.Name = kp.AlgorithmName()
-	resp.Key.Algorithm.Version = kp.AlgorithmVersion()
-	resp.Key.Meta = kp.Meta()
+	resp.PublicKey = kp.PublicKey()
+	resp.Algorithm.Name = kp.AlgorithmName()
+	resp.Algorithm.Version = kp.AlgorithmVersion()
+	resp.Meta = kp.Meta()
 
 	return resp, nil
 }
@@ -210,7 +182,6 @@ type DescribeKeyRequest struct {
 
 type DescribeKeyResponse struct {
 	PublicKey string `json:"publicKey"`
-
 	Algorithm struct {
 		Name    string `json:"name"`
 		Version uint32 `json:"version"`
@@ -499,6 +470,52 @@ func ListWallets(store Store) (*ListWalletsResponse, error) {
 	return resp, nil
 }
 
+type SignCommandRequest struct {
+	Wallet        string
+	Passphrase    string
+	TxBlockHeight uint64
+	Request       *walletpb.SubmitTransactionRequest
+}
+
+type SignCommandResponse struct {
+	Base64Transaction string `json:"base64Transaction"`
+}
+
+func SignCommand(store Store, req *SignCommandRequest) (*SignCommandResponse, error) {
+	w, err := getWallet(store, req.Wallet, req.Passphrase)
+	if err != nil {
+		return nil, err
+	}
+
+	data, err := wcommands.ToMarshaledInputData(req.Request, req.TxBlockHeight)
+	if err != nil {
+		return nil, fmt.Errorf("couldn't marshal input data: %w", err)
+	}
+
+	pubKey := req.Request.GetPubKey()
+	signature, err := w.SignTx(pubKey, data)
+	if err != nil {
+		return nil, fmt.Errorf("couldn't sign transaction: %w", err)
+	}
+
+	protoSignature := &commandspb.Signature{
+		Value:   signature.Value,
+		Algo:    signature.Algo,
+		Version: signature.Version,
+	}
+
+	tx := commands.NewTransaction(pubKey, data, protoSignature)
+
+	rawTx, err := proto.Marshal(tx)
+	if err != nil {
+		return nil, fmt.Errorf("couldn't marshal transaction: %w", err)
+	}
+
+	return &SignCommandResponse{
+		Base64Transaction: base64.StdEncoding.EncodeToString(rawTx),
+	}, nil
+}
+
 type SignMessageRequest struct {
 	Wallet     string
 	PubKey     string


Test patch:
diff --git a/cmd/command_send_test.go b/cmd/command_send_test.go
index ca1d86d..96002d8 100644
--- a/cmd/command_send_test.go
+++ b/cmd/command_send_test.go
@@ -187,7 +187,7 @@ func testSendCommandFlagsMalformedRequestFails(t *testing.T) {
 
 	// given
 	f := newSendCommandFlags(t, testDir)
-	f.RawCommand = `<request>Not gonna support that</request>`
+	f.RawCommand = vgrand.RandomStr(5)
 
 	// when
 	req, err := f.Validate()
diff --git a/cmd/command_sign_test.go b/cmd/command_sign_test.go
new file mode 100644
index 0000000..8c19953
--- /dev/null
+++ b/cmd/command_sign_test.go
@@ -0,0 +1,188 @@
+package cmd_test
+
+import (
+	"testing"
+
+	"code.vegaprotocol.io/protos/vega"
+	v1 "code.vegaprotocol.io/protos/vega/commands/v1"
+	walletpb "code.vegaprotocol.io/protos/vega/wallet/v1"
+	vgrand "code.vegaprotocol.io/shared/libs/rand"
+	"code.vegaprotocol.io/vegawallet/cmd"
+	"code.vegaprotocol.io/vegawallet/cmd/flags"
+	wcommands "code.vegaprotocol.io/vegawallet/wallet"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+func TestSignCommandFlags(t *testing.T) {
+	t.Run("Valid flags succeeds", testSignCommandFlagsValidFlagsSucceeds)
+	t.Run("Missing wallet fails", testSignCommandFlagsMissingWalletFails)
+	t.Run("Missing public key fails", testSignCommandFlagsMissingPubKeyFails)
+	t.Run("Missing tx height fails", testSignCommandFlagsMissingTxBlockHeightFails)
+	t.Run("Missing request fails", testSignCommandFlagsMissingRequestFails)
+	t.Run("Malformed request fails", testSignCommandFlagsMalformedRequestFails)
+	t.Run("Invalid request fails", testSignCommandFlagsInvalidRequestFails)
+	t.Run("Request with public key set in it fails", testSignCommandFlagsRequestWithPubKeyFails)
+}
+
+func testSignCommandFlagsValidFlagsSucceeds(t *testing.T) {
+	testDir := t.TempDir()
+
+	// given
+	passphrase, passphraseFilePath := NewPassphraseFile(t, testDir)
+	walletName := vgrand.RandomStr(10)
+	pubKey := vgrand.RandomStr(20)
+
+	f := &cmd.SignCommandFlags{
+		Wallet:         walletName,
+		PubKey:         pubKey,
+		PassphraseFile: passphraseFilePath,
+		TxBlockHeight:  150,
+		RawCommand:     `{"voteSubmission": {"proposalId": "some-id", "value": "VALUE_YES"}}`,
+	}
+
+	expectedReq := &wcommands.SignCommandRequest{
+		Wallet:        walletName,
+		Passphrase:    passphrase,
+		TxBlockHeight: 150,
+		Request: &walletpb.SubmitTransactionRequest{
+			PubKey:    pubKey,
+			Propagate: true,
+			Command: &walletpb.SubmitTransactionRequest_VoteSubmission{
+				VoteSubmission: &v1.VoteSubmission{
+					ProposalId: "some-id",
+					Value:      vega.Vote_VALUE_YES,
+				},
+			},
+		},
+	}
+
+	// when
+	req, err := f.Validate()
+
+	// then
+	require.NoError(t, err)
+	require.NotNil(t, req)
+	assert.Equal(t, expectedReq, req)
+}
+
+func testSignCommandFlagsMissingWalletFails(t *testing.T) {
+	testDir := t.TempDir()
+
+	// given
+	f := newSignCommandFlags(t, testDir)
+	f.Wallet = ""
+
+	// when
+	req, err := f.Validate()
+
+	// then
+	assert.ErrorIs(t, err, flags.FlagMustBeSpecifiedError("wallet"))
+	assert.Nil(t, req)
+}
+
+func testSignCommandFlagsMissingPubKeyFails(t *testing.T) {
+	testDir := t.TempDir()
+
+	// given
+	f := newSignCommandFlags(t, testDir)
+	f.PubKey = ""
+
+	// when
+	req, err := f.Validate()
+
+	// then
+	assert.ErrorIs(t, err, flags.FlagMustBeSpecifiedError("pubkey"))
+	assert.Nil(t, req)
+}
+
+func testSignCommandFlagsMissingTxBlockHeightFails(t *testing.T) {
+	testDir := t.TempDir()
+
+	// given
+	f := newRotateKeyFlags(t, testDir)
+	f.TxBlockHeight = 0
+
+	// when
+	req, err := f.Validate()
+
+	// then
+	assert.ErrorIs(t, err, flags.FlagMustBeSpecifiedError("tx-height"))
+	assert.Nil(t, req)
+}
+
+func testSignCommandFlagsMissingRequestFails(t *testing.T) {
+	testDir := t.TempDir()
+
+	// given
+	f := newSignCommandFlags(t, testDir)
+	f.RawCommand = ""
+
+	// when
+	req, err := f.Validate()
+
+	// then
+	assert.ErrorIs(t, err, flags.ArgMustBeSpecifiedError("command"))
+	assert.Nil(t, req)
+}
+
+func testSignCommandFlagsMalformedRequestFails(t *testing.T) {
+	testDir := t.TempDir()
+
+	// given
+	f := newSignCommandFlags(t, testDir)
+	f.RawCommand = vgrand.RandomStr(5)
+
+	// when
+	req, err := f.Validate()
+
+	// then
+	assert.Error(t, err)
+	assert.Nil(t, req)
+}
+
+func testSignCommandFlagsInvalidRequestFails(t *testing.T) {
+	testDir := t.TempDir()
+
+	// given
+	f := newSignCommandFlags(t, testDir)
+	f.RawCommand = `{"voteSubmission": {}}`
+
+	// when
+	req, err := f.Validate()
+
+	// then
+	assert.Error(t, err)
+	assert.Nil(t, req)
+}
+
+func testSignCommandFlagsRequestWithPubKeyFails(t *testing.T) {
+	testDir := t.TempDir()
+
+	// given
+	f := newSignCommandFlags(t, testDir)
+	f.RawCommand = `{"pubKey": "qwerty123456", "voteSubmission": {"proposalId": "some-id", "value": "VALUE_YES"}}`
+
+	// when
+	req, err := f.Validate()
+
+	// then
+	assert.ErrorIs(t, err, cmd.ErrDoNotSetPubKeyInCommand)
+	assert.Nil(t, req)
+}
+
+func newSignCommandFlags(t *testing.T, testDir string) *cmd.SignCommandFlags {
+	t.Helper()
+
+	_, passphraseFilePath := NewPassphraseFile(t, testDir)
+	walletName := vgrand.RandomStr(10)
+	pubKey := vgrand.RandomStr(20)
+
+	return &cmd.SignCommandFlags{
+		RawCommand:     `{"voteSubmission": {"proposalId": "some-id", "value": "VALUE_YES"}}`,
+		Wallet:         walletName,
+		PubKey:         pubKey,
+		TxBlockHeight:  150,
+		PassphraseFile: passphraseFilePath,
+	}
+}
diff --git a/cmd/key_describe_test.go b/cmd/key_describe_test.go
index 677d7fc..0a4a463 100644
--- a/cmd/key_describe_test.go
+++ b/cmd/key_describe_test.go
@@ -20,8 +20,6 @@ func TestDescribeKeyFlags(t *testing.T) {
 func testKeyDescribeValidFlagsSucceeds(t *testing.T) {
 	// given
 	testDir := t.TempDir()
-
-	// given
 	passphrase, passphraseFilePath := NewPassphraseFile(t, testDir)
 	walletName := vgrand.RandomStr(10)
 	pubKey := vgrand.RandomStr(10)
diff --git a/cmd/key_generate_test.go b/cmd/key_generate_test.go
index 43464ce..e41859e 100644
--- a/cmd/key_generate_test.go
+++ b/cmd/key_generate_test.go
@@ -19,11 +19,13 @@ func TestGenerateKeyFlags(t *testing.T) {
 
 func testGenerateKeyFlagsValidFlagsSucceeds(t *testing.T) {
 	// given
+	testDir := t.TempDir()
 	walletName := vgrand.RandomStr(10)
+	passphrase, passphraseFilePath := NewPassphraseFile(t, testDir)
 
 	f := &cmd.GenerateKeyFlags{
 		Wallet:         walletName,
-		PassphraseFile: "/some/fake/path",
+		PassphraseFile: passphraseFilePath,
 		RawMetadata:    []string{"name:my-wallet", "role:validation"},
 	}
 
@@ -33,8 +35,7 @@ func testGenerateKeyFlagsValidFlagsSucceeds(t *testing.T) {
 			{Key: "name", Value: "my-wallet"},
 			{Key: "role", Value: "validation"},
 		},
-		// This is expected as it's not set during flags validation
-		Passphrase: "",
+		Passphrase: passphrase,
 	}
 
 	// when
@@ -74,9 +75,11 @@ func testGenerateKeyFlagsInvalidMetadataFails(t *testing.T) {
 
 func newGenerateKeyFlags(t *testing.T) *cmd.GenerateKeyFlags {
 	t.Helper()
+	testDir := t.TempDir()
+	_, passphraseFilePath := NewPassphraseFile(t, testDir)
 	return &cmd.GenerateKeyFlags{
-		Wallet:         vgrand.RandomStr(10),
-		PassphraseFile: "/some/fake/path",
+		Wallet:         vgrand.RandomStr(5),
+		PassphraseFile: passphraseFilePath,
 		RawMetadata:    []string{"name:my-wallet", "role:validation"},
 	}
 }
diff --git a/cmd/tx_send_test.go b/cmd/tx_send_test.go
index c1ebbc2..3727861 100644
--- a/cmd/tx_send_test.go
+++ b/cmd/tx_send_test.go
@@ -131,7 +131,7 @@ func testSendTxFlagsMissingTxFails(t *testing.T) {
 func testSendTxFlagsMalformedTxFails(t *testing.T) {
 	// given
 	f := newSendTxFlags(t)
-	f.RawTx = `<request>Not gonna support that</request>`
+	f.RawTx = vgrand.RandomStr(5)
 
 	// when
 	req, err := f.Validate()
diff --git a/network/network_test.go b/network/network_test.go
new file mode 100644
index 0000000..54daa46
--- /dev/null
+++ b/network/network_test.go
@@ -0,0 +1,106 @@
+package network_test
+
+import (
+	"testing"
+
+	"code.vegaprotocol.io/vegawallet/network"
+	"github.com/stretchr/testify/require"
+)
+
+func Test(t *testing.T) {
+	t.Run("Ensure network can connect to a gRPC node fails", testEnsureNetworkCanConnectGRPCNodeFails)
+	t.Run("Ensure network can connect to a console fails", testEnsureNetworkCanConnectConsoleFails)
+	t.Run("Ensure network can connect to a token dApp fails", testEnsureNetworkCanConnectTokenDAppFails)
+}
+
+func testEnsureNetworkCanConnectGRPCNodeFails(t *testing.T) {
+	// given
+	net := &network.Network{
+		API: network.APIConfig{GRPC: network.GRPCConfig{
+			Hosts:   nil,
+			Retries: 0,
+		}},
+	}
+
+	// when
+	err := net.EnsureCanConnectGRPCNode()
+
+	// then
+	require.ErrorIs(t, err, network.ErrNetworkDoesNotHaveGRPCHostConfigured)
+}
+
+func testEnsureNetworkCanConnectConsoleFails(t *testing.T) {
+	tcs := []struct {
+		name    string
+		err     error
+		network *network.Network
+	}{
+		{
+			name: "without host",
+			err:  network.ErrNetworkDoesNotHaveHostConfiguredForConsole,
+			network: &network.Network{
+				Console: network.ConsoleConfig{
+					URL:       "",
+					LocalPort: 1234,
+				},
+			},
+		}, {
+			name: "without local port",
+			err:  network.ErrNetworkDoesNotHaveLocalPortConfiguredForConsole,
+			network: &network.Network{
+				Console: network.ConsoleConfig{
+					URL:       "https://example.com",
+					LocalPort: 0,
+				},
+			},
+		},
+	}
+
+	for _, tc := range tcs {
+		t.Run(tc.name, func(tt *testing.T) {
+			// when
+			err := tc.network.EnsureCanConnectConsole()
+
+			// then
+			require.ErrorIs(tt, err, tc.err)
+		})
+	}
+}
+
+func testEnsureNetworkCanConnectTokenDAppFails(t *testing.T) {
+	tcs := []struct {
+		name    string
+		err     error
+		network *network.Network
+	}{
+		{
+			name: "without host",
+			err:  network.ErrNetworkDoesNotHaveHostConfiguredForTokenDApp,
+			network: &network.Network{
+				TokenDApp: network.TokenDAppConfig{
+					URL:       "",
+					LocalPort: 1234,
+				},
+			},
+		}, {
+			name: "without host",
+			err:  network.ErrNetworkDoesNotHaveLocalPortConfiguredForTokenDApp,
+			network: &network.Network{
+				TokenDApp: network.TokenDAppConfig{
+					URL:       "https://example.com",
+					LocalPort: 0,
+				},
+			},
+		},
+	}
+
+	for _, tc := range tcs {
+		t.Run(tc.name, func(tt *testing.T) {
+			// when
+			err := tc.network.EnsureCanConnectTokenDApp()
+
+			// then
+			require.ErrorIs(tt, err, tc.err)
+		})
+	}
+}
diff --git a/network/store/v1/store_test.go b/network/store/v1/store_test.go
index 7d97187..cb0aa65 100644
--- a/network/store/v1/store_test.go
+++ b/network/store/v1/store_test.go
@@ -18,12 +18,10 @@ func TestFileStoreV1(t *testing.T) {
 	t.Run("New store succeeds", testNewStoreSucceeds)
 	t.Run("Saving already existing network succeeds", testFileStoreV1SaveAlreadyExistingNetworkSucceeds)
 	t.Run("Saving network succeeds", testFileStoreV1SaveNetworkSucceeds)
-	t.Run("Saving legacy network succeeds", testFileStoreV1SaveLegacyNetworkSucceeds)
 	t.Run("Verifying non-existing network fails", testFileStoreV1VerifyingNonExistingNetworkFails)
 	t.Run("Verifying existing network succeeds", testFileStoreV1VerifyingExistingNetworkSucceeds)
 	t.Run("Getting non-existing network fails", testFileStoreV1GetNonExistingNetworkFails)
 	t.Run("Getting existing network succeeds", testFileStoreV1GetExistingNetworkSucceeds)
-	t.Run("Getting legacy network succeeds", testFileStoreV1GetLegacyNetworkSucceeds)
 	t.Run("Getting network path succeeds", testFileStoreV1GetNetworkPathSucceeds)
 	t.Run("Getting networks path succeeds", testFileStoreV1GetNetworksPathSucceeds)
 	t.Run("Listing networks succeeds", testFileStoreV1ListingNetworksSucceeds)
@@ -116,47 +114,6 @@ func testFileStoreV1SaveNetworkSucceeds(t *testing.T) {
 	assert.Equal(t, net, returnedNet)
 }
 
-func testFileStoreV1SaveLegacyNetworkSucceeds(t *testing.T) {
-	vegaHome := newVegaHome(t)
-
-	// given
-	s := initialiseFromPath(t, vegaHome)
-	net := &network.Network{
-		Name: "test",
-		Nodes: network.GRPCConfig{
-			Hosts:   []string{"node-1", "node-2"},
-			Retries: 5,
-		},
-		API: network.APIConfig{
-			GRPC: network.GRPCConfig{},
-		},
-	}
-
-	// when
-	err := s.SaveNetwork(net)
-
-	// then
-	require.NoError(t, err)
-	vgtest.AssertFileAccess(t, networkPath(t, vegaHome, net.Name))
-
-	// when
-	returnedNet, err := s.GetNetwork("test")
-
-	// then
-	require.NoError(t, err)
-	expectedNet := network.Network{
-		Name:  "test",
-		Nodes: network.GRPCConfig{},
-		API: network.APIConfig{
-			GRPC: network.GRPCConfig{
-				Hosts:   []string{"node-1", "node-2"},
-				Retries: 5,
-			},
-		},
-	}
-	assert.Equal(t, expectedNet, *returnedNet)
-}
-
 func testFileStoreV1VerifyingNonExistingNetworkFails(t *testing.T) {
 	vegaHome := newVegaHome(t)
 
@@ -233,47 +190,6 @@ func testFileStoreV1GetExistingNetworkSucceeds(t *testing.T) {
 	assert.Equal(t, net, returnedNet)
 }
 
-func testFileStoreV1GetLegacyNetworkSucceeds(t *testing.T) {
-	vegaHome := newVegaHome(t)
-
-	// given
-	s := initialiseFromPath(t, vegaHome)
-	legacyNet := &network.Network{
-		Name: "test",
-		Nodes: network.GRPCConfig{
-			Hosts:   []string{"node-1", "node-2"},
-			Retries: 5,
-		},
-		API: network.APIConfig{
-			GRPC: network.GRPCConfig{},
-		},
-	}
-
-	// when
-	err := s.SaveNetwork(legacyNet)
-
-	// then
-	require.NoError(t, err)
-	vgtest.AssertFileAccess(t, networkPath(t, vegaHome, legacyNet.Name))
-
-	// when
-	returnedNet, err := s.GetNetwork("test")
-
-	// then
-	require.NoError(t, err)
-	expectedNet := network.Network{
-		Name:  "test",
-		Nodes: network.GRPCConfig{},
-		API: network.APIConfig{
-			GRPC: network.GRPCConfig{
-				Hosts:   []string{"node-1", "node-2"},
-				Retries: 5,
-			},
-		},
-	}
-	assert.Equal(t, expectedNet, *returnedNet)
-}
-
 func testFileStoreV1GetNetworkPathSucceeds(t *testing.T) {
 	vegaHome := newVegaHome(t)
 
diff --git a/tests/commands_for_test.go b/tests/commands_for_test.go
index 09def62..d80156a 100644
--- a/tests/commands_for_test.go
+++ b/tests/commands_for_test.go
@@ -72,23 +72,15 @@ func KeyAnnotate(t *testing.T, args []string) error {
 }
 
 type GenerateKeyResponse struct {
-	Wallet struct {
-		Name     string `json:"name"`
-		Version  uint32 `json:"version"`
-		FilePath string `json:"filePath"`
-		Mnemonic string `json:"mnemonic,omitempty"`
-	} `json:"wallet"`
-	Key struct {
-		PublicKey string `json:"publicKey"`
-		Algorithm struct {
-			Name    string `json:"name"`
-			Version uint32 `json:"version"`
-		} `json:"algorithm"`
-		Meta []struct {
-			Key   string `json:"key"`
-			Value string `json:"value"`
-		} `json:"meta"`
-	} `json:"key"`
+	PublicKey string `json:"publicKey"`
+	Algorithm struct {
+		Name    string `json:"name"`
+		Version uint32 `json:"version"`
+	} `json:"algorithm"`
+	Meta []struct {
+		Key   string `json:"key"`
+		Value string `json:"value"`
+	} `json:"meta"`
 }
 
 func KeyGenerate(t *testing.T, args []string) (*GenerateKeyResponse, error) {
@@ -115,13 +107,9 @@ func AssertGenerateKey(t *testing.T, resp *GenerateKeyResponse) *GenerateKeyAsse
 	t.Helper()
 
 	assert.NotNil(t, resp)
-	assert.NotEmpty(t, resp.Wallet.Name)
-	assert.NotEmpty(t, resp.Wallet.Version)
-	assert.NotEmpty(t, resp.Wallet.FilePath)
-	assert.FileExists(t, resp.Wallet.FilePath)
-	assert.NotEmpty(t, resp.Key.PublicKey)
-	assert.Equal(t, "vega/ed25519", resp.Key.Algorithm.Name)
-	assert.Equal(t, uint32(1), resp.Key.Algorithm.Version)
+	assert.NotEmpty(t, resp.PublicKey)
+	assert.Equal(t, "vega/ed25519", resp.Algorithm.Name)
+	assert.Equal(t, uint32(1), resp.Algorithm.Version)
 
 	return &GenerateKeyAssertion{
 		t:    t,
@@ -129,27 +117,9 @@ func AssertGenerateKey(t *testing.T, resp *GenerateKeyResponse) *GenerateKeyAsse
 	}
 }
 
-// Deprecated: key generate will not generate wallet anymore.
-func (a *GenerateKeyAssertion) WithWalletCreation() *GenerateKeyAssertion {
-	assert.NotEmpty(a.t, a.resp.Wallet.Mnemonic)
-	return a
-}
-
-// Deprecated: key generate will not generate wallet anymore.
-func (a *GenerateKeyAssertion) WithName(expected string) *GenerateKeyAssertion {
-	assert.Equal(a.t, expected, a.resp.Wallet.Name)
-	return a
-}
-
-// Deprecated: key generate will not generate wallet anymore.
-func (a *GenerateKeyAssertion) WithVersion(expected uint32) *GenerateKeyAssertion {
-	assert.Equal(a.t, expected, a.resp.Wallet.Version)
-	return a
-}
-
 func (a *GenerateKeyAssertion) WithMeta(expected map[string]string) *GenerateKeyAssertion {
 	meta := map[string]string{}
-	for _, m := range a.resp.Key.Meta {
+	for _, m := range a.resp.Meta {
 		meta[m.Key] = m.Value
 	}
 	assert.Equal(a.t, expected, meta)
@@ -157,12 +127,7 @@ func (a *GenerateKeyAssertion) WithMeta(expected map[string]string) *GenerateKey
 }
 
 func (a *GenerateKeyAssertion) WithPublicKey(expected string) *GenerateKeyAssertion {
-	assert.Equal(a.t, expected, a.resp.Key.PublicKey)
-	return a
-}
-
-func (a *GenerateKeyAssertion) LocatedUnder(home string) *GenerateKeyAssertion {
-	assert.True(a.t, strings.HasPrefix(a.resp.Wallet.FilePath, home), "wallet has not been generated under home directory")
+	assert.Equal(a.t, expected, a.resp.PublicKey)
 	return a
 }
 
@@ -419,6 +384,46 @@ func (a *ImportNetworkAssertion) LocatedUnder(home string) *ImportNetworkAsserti
 	return a
 }
 
+type LocateNetworksResponse struct {
+	Path string `json:"path"`
+}
+
+func NetworkLocate(t *testing.T, args []string) (*LocateNetworksResponse, error) {
+	t.Helper()
+	argsWithCmd := []string{"network", "locate"}
+	argsWithCmd = append(argsWithCmd, args...)
+	output, err := ExecuteCmd(t, argsWithCmd)
+	if err != nil {
+		return nil, err
+	}
+	resp := &LocateNetworksResponse{}
+	if err := json.Unmarshal(output, resp); err != nil {
+		t.Fatalf("couldn't unmarshal command output: %v", err)
+	}
+	return resp, nil
+}
+
+type LocateNetworkAssertion struct {
+	t    *testing.T
+	resp *LocateNetworksResponse
+}
+
+func AssertLocateNetwork(t *testing.T, resp *LocateNetworksResponse) *LocateNetworkAssertion {
+	t.Helper()
+
+	assert.NotNil(t, resp)
+
+	return &LocateNetworkAssertion{
+		t:    t,
+		resp: resp,
+	}
+}
+
+func (a *LocateNetworkAssertion) LocatedUnder(p string) *LocateNetworkAssertion {
+	assert.True(a.t, strings.HasPrefix(a.resp.Path, p), "path returned doesn't start with home path")
+	return a
+}
+
 type ListNetworksResponse struct {
 	Networks []string `json:"networks"`
 }
@@ -567,13 +572,49 @@ func (d *DescribeNetworkAssertion) WithRESTConfig(hosts []string) *DescribeNetwo
 	return d
 }
 
+type SignCommandResponse struct {
+	Transaction string `json:"base64Transaction"`
+}
+
+func SignCommand(t *testing.T, args []string) (*SignCommandResponse, error) {
+	t.Helper()
+	argsWithCmd := []string{"command", "sign"}
+	argsWithCmd = append(argsWithCmd, args...)
+	output, err := ExecuteCmd(t, argsWithCmd)
+	if err != nil {
+		return nil, err
+	}
+	resp := &SignCommandResponse{}
+	if err := json.Unmarshal(output, resp); err != nil {
+		t.Fatalf("couldn't unmarshal command output: %v", err)
+	}
+	return resp, nil
+}
+
+type SignCommandAssertion struct {
+	t    *testing.T
+	resp *SignCommandResponse
+}
+
+func AssertSignCommand(t *testing.T, resp *SignCommandResponse) *SignCommandAssertion {
+	t.Helper()
+
+	assert.NotNil(t, resp)
+	assert.NotEmpty(t, resp.Transaction)
+
+	return &SignCommandAssertion{
+		t:    t,
+		resp: resp,
+	}
+}
+
 type SignMessageResponse struct {
 	Signature string `json:"signature"`
 }
 
-func Sign(t *testing.T, args []string) (*SignMessageResponse, error) {
+func SignMessage(t *testing.T, args []string) (*SignMessageResponse, error) {
 	t.Helper()
-	argsWithCmd := []string{"sign"}
+	argsWithCmd := []string{"message", "sign"}
 	argsWithCmd = append(argsWithCmd, args...)
 	output, err := ExecuteCmd(t, argsWithCmd)
 	if err != nil {
@@ -586,24 +627,24 @@ func Sign(t *testing.T, args []string) (*SignMessageResponse, error) {
 	return resp, nil
 }
 
-type SignAssertion struct {
+type SignMessageAssertion struct {
 	t    *testing.T
 	resp *SignMessageResponse
 }
 
-func AssertSign(t *testing.T, resp *SignMessageResponse) *SignAssertion {
+func AssertSignMessage(t *testing.T, resp *SignMessageResponse) *SignMessageAssertion {
 	t.Helper()
 
 	assert.NotNil(t, resp)
 	assert.NotEmpty(t, resp.Signature)
 
-	return &SignAssertion{
+	return &SignMessageAssertion{
 		t:    t,
 		resp: resp,
 	}
 }
 
-func (a *SignAssertion) WithSignature(expected string) *SignAssertion {
+func (a *SignMessageAssertion) WithSignature(expected string) *SignMessageAssertion {
 	assert.Equal(a.t, expected, a.resp.Signature)
 	return a
 }
@@ -612,9 +653,9 @@ type VerifyMessageResponse struct {
 	IsValid bool `json:"isValid"`
 }
 
-func Verify(t *testing.T, args []string) (*VerifyMessageResponse, error) {
+func VerifyMessage(t *testing.T, args []string) (*VerifyMessageResponse, error) {
 	t.Helper()
-	argsWithCmd := []string{"verify"}
+	argsWithCmd := []string{"message", "verify"}
 	argsWithCmd = append(argsWithCmd, args...)
 	output, err := ExecuteCmd(t, argsWithCmd)
 	if err != nil {
@@ -632,7 +673,7 @@ type VerifyAssertion struct {
 	resp *VerifyMessageResponse
 }
 
-func AssertVerify(t *testing.T, resp *VerifyMessageResponse) *VerifyAssertion {
+func AssertVerifyMessage(t *testing.T, resp *VerifyMessageResponse) *VerifyAssertion {
 	t.Helper()
 
 	assert.NotNil(t, resp)
diff --git a/tests/helpers_for_test.go b/tests/helpers_for_test.go
index 5cd85f3..373123d 100644
--- a/tests/helpers_for_test.go
+++ b/tests/helpers_for_test.go
@@ -61,3 +61,7 @@ URL = "console.example.com"
 LocalPort = 1847
 `, name)
 }
+
+func RandomPath() string {
+	return filepath.Join(vgrand.RandomStr(5), vgrand.RandomStr(5))
+}
diff --git a/tests/sc_create_wallet_test.go b/tests/sc_create_wallet_test.go
index 56e3f28..3ebdf77 100644
--- a/tests/sc_create_wallet_test.go
+++ b/tests/sc_create_wallet_test.go
@@ -56,57 +56,3 @@ func TestCreateWallet(t *testing.T) {
 	require.Len(t, listKeysResp.Keys, 1)
 	assert.Equal(t, listKeysResp.Keys[0].PublicKey, createWalletResp.Key.PublicKey)
 }
-
-// Deprecated: key generate will not generate wallet anymore.
-func TestCreateWalletUsingKeyGenerate(t *testing.T) {
-	// given
-	home := t.TempDir()
-	_, passphraseFilePath := NewPassphraseFile(t, home)
-	walletName := vgrand.RandomStr(5)
-
-	// when
-	generateKeyResp, err := KeyGenerate(t, []string{
-		"--home", home,
-		"--output", "json",
-		"--wallet", walletName,
-		"--passphrase-file", passphraseFilePath,
-		"--meta", "name:key-1,role:validation",
-	})
-
-	// then
-	require.NoError(t, err)
-	AssertGenerateKey(t, generateKeyResp).
-		WithWalletCreation().
-		WithName(walletName).
-		WithVersion(2).
-		WithMeta(map[string]string{"name": "key-1", "role": "validation"}).
-		LocatedUnder(home)
-
-	// when
-	walletInfoResp, err := WalletInfo(t, []string{
-		"--home", home,
-		"--output", "json",
-		"--wallet", walletName,
-		"--passphrase-file", passphraseFilePath,
-	})
-
-	// then
-	require.NoError(t, err)
-	AssertWalletInfo(t, walletInfoResp).
-		IsHDWallet().
-		WithLatestVersion()
-
-	// when
-	listKeysResp, err := KeyList(t, []string{
-		"--home", home,
-		"--output", "json",
-		"--wallet", walletName,
-		"--passphrase-file", passphraseFilePath,
-	})
-
-	// then
-	require.NoError(t, err)
-	require.NotNil(t, listKeysResp)
-	require.Len(t, listKeysResp.Keys, 1)
-	assert.Equal(t, listKeysResp.Keys[0].PublicKey, generateKeyResp.Key.PublicKey)
-}
diff --git a/tests/sc_generate_keys_test.go b/tests/sc_generate_keys_test.go
index 536b251..a98d59e 100644
--- a/tests/sc_generate_keys_test.go
+++ b/tests/sc_generate_keys_test.go
@@ -55,8 +55,7 @@ func TestGenerateKey(t *testing.T) {
 	// then
 	require.NoError(t, err)
 	AssertGenerateKey(t, generateKeyResp).
-		WithMeta(map[string]string{"name": "key-2", "role": "validation"}).
-		LocatedUnder(home)
+		WithMeta(map[string]string{"name": "key-2", "role": "validation"})
 
 	// when
 	descResp, err = KeyDescribe(t, []string{
@@ -64,7 +63,7 @@ func TestGenerateKey(t *testing.T) {
 		"--output", "json",
 		"--wallet", walletName,
 		"--passphrase-file", passphraseFilePath,
-		"--pubkey", generateKeyResp.Key.PublicKey,
+		"--pubkey", generateKeyResp.PublicKey,
 	})
 
 	// then
diff --git a/tests/sc_import_wallet_test.go b/tests/sc_import_wallet_test.go
index d91968e..0c8c8ce 100644
--- a/tests/sc_import_wallet_test.go
+++ b/tests/sc_import_wallet_test.go
@@ -71,8 +71,7 @@ func TestImportWalletV1(t *testing.T) {
 	require.NoError(t, err)
 	AssertGenerateKey(t, generateKeyResp).
 		WithMeta(map[string]string{"name": "key-1", "role": "validation"}).
-		WithPublicKey("de998bab8d15a6f6b9584251ff156c2424ccdf1de8ba00e4933595773e9e00dc").
-		LocatedUnder(home)
+		WithPublicKey("de998bab8d15a6f6b9584251ff156c2424ccdf1de8ba00e4933595773e9e00dc")
 
 	// when
 	listKeysResp2, err := KeyList(t, []string{
@@ -152,8 +151,7 @@ func TestImportWalletV2(t *testing.T) {
 	require.NoError(t, err)
 	AssertGenerateKey(t, generateKeyResp).
 		WithMeta(map[string]string{"name": "key-1", "role": "validation"}).
-		WithPublicKey("988eae323a07f12363c17025c23ee58ea32ac3912398e16bb0b56969f57adc52").
-		LocatedUnder(home)
+		WithPublicKey("988eae323a07f12363c17025c23ee58ea32ac3912398e16bb0b56969f57adc52")
 
 	// when
 	listKeysResp2, err := KeyList(t, []string{
diff --git a/tests/sc_list_keys_test.go b/tests/sc_list_keys_test.go
index da9a11f..70a529e 100644
--- a/tests/sc_list_keys_test.go
+++ b/tests/sc_list_keys_test.go
@@ -53,8 +53,7 @@ func TestListKeys(t *testing.T) {
 	// then
 	require.NoError(t, err)
 	AssertGenerateKey(t, generateKeyResp).
-		WithMeta(map[string]string{"name": DefaultMetaName(t, walletName, 2)}).
-		LocatedUnder(home)
+		WithMeta(map[string]string{"name": DefaultMetaName(t, walletName, 2)})
 
 	// when
 	listKeysResp2, err := KeyList(t, []string{
@@ -69,5 +68,5 @@ func TestListKeys(t *testing.T) {
 	require.NotNil(t, listKeysResp2)
 	require.Len(t, listKeysResp2.Keys, 2)
 	assert.Equal(t, listKeysResp2.Keys[0].PublicKey, createWalletResp.Key.PublicKey)
-	assert.Equal(t, listKeysResp2.Keys[1].PublicKey, generateKeyResp.Key.PublicKey)
+	assert.Equal(t, listKeysResp2.Keys[1].PublicKey, generateKeyResp.PublicKey)
 }
diff --git a/tests/sc_locate_networks_test.go b/tests/sc_locate_networks_test.go
new file mode 100644
index 0000000..561552b
--- /dev/null
+++ b/tests/sc_locate_networks_test.go
@@ -0,0 +1,23 @@
+package tests_test
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/require"
+)
+
+func TestLocateNetworks(t *testing.T) {
+	// given
+	home := RandomPath()
+
+	// when
+	locateNetworkResp, err := NetworkLocate(t, []string{
+		"--home", home,
+		"--output", "json",
+	})
+
+	// then
+	require.NoError(t, err)
+	AssertLocateNetwork(t, locateNetworkResp).
+		LocatedUnder(home)
+}
diff --git a/tests/sc_rotate_key_test.go b/tests/sc_rotate_key_test.go
index 06c0f75..c113bce 100644
--- a/tests/sc_rotate_key_test.go
+++ b/tests/sc_rotate_key_test.go
@@ -41,8 +41,7 @@ func TestRotateKeySucceeds(t *testing.T) {
 	// then
 	require.NoError(t, err)
 	AssertGenerateKey(t, generateKeyResp).
-		WithMeta(map[string]string{"name": "key-2", "role": "validation"}).
-		LocatedUnder(home)
+		WithMeta(map[string]string{"name": "key-2", "role": "validation"})
 
 	// when
 	resp, err := KeyRotate(t, []string{
@@ -51,7 +50,7 @@ func TestRotateKeySucceeds(t *testing.T) {
 		"--wallet", walletName,
 		"--passphrase-file", passphraseFilePath,
 		"--current-pubkey", createWalletResp.Key.PublicKey,
-		"--new-pubkey", generateKeyResp.Key.PublicKey,
+		"--new-pubkey", generateKeyResp.PublicKey,
 		"--tx-height", "20",
 		"--target-height", "25",
 	})
@@ -93,8 +92,7 @@ func TestRotateKeyFailsOnTaintedPublicKey(t *testing.T) {
 	// then
 	require.NoError(t, err)
 	AssertGenerateKey(t, generateKeyResp).
-		WithMeta(map[string]string{"name": "key-2", "role": "validation"}).
-		LocatedUnder(home)
+		WithMeta(map[string]string{"name": "key-2", "role": "validation"})
 
 	// when
 	err = KeyTaint(t, []string{
@@ -102,7 +100,7 @@ func TestRotateKeyFailsOnTaintedPublicKey(t *testing.T) {
 		"--output", "json",
 		"--wallet", walletName,
 		"--passphrase-file", passphraseFilePath,
-		"--pubkey", generateKeyResp.Key.PublicKey,
+		"--pubkey", generateKeyResp.PublicKey,
 	})
 
 	// then
@@ -115,7 +113,7 @@ func TestRotateKeyFailsOnTaintedPublicKey(t *testing.T) {
 		"--wallet", walletName,
 		"--passphrase-file", passphraseFilePath,
 		"--current-pubkey", createWalletResp.Key.PublicKey,
-		"--new-pubkey", generateKeyResp.Key.PublicKey,
+		"--new-pubkey", generateKeyResp.PublicKey,
 		"--tx-height", "20",
 		"--target-height", "25",
 	})
diff --git a/tests/sc_sign_and_verify_command_test.go b/tests/sc_sign_and_verify_command_test.go
new file mode 100644
index 0000000..a81a37c
--- /dev/null
+++ b/tests/sc_sign_and_verify_command_test.go
@@ -0,0 +1,126 @@
+package tests_test
+
+import (
+	"testing"
+
+	vgrand "code.vegaprotocol.io/shared/libs/rand"
+	"github.com/stretchr/testify/require"
+)
+
+func TestSignCommand(t *testing.T) {
+	// given
+	home := t.TempDir()
+	_, passphraseFilePath := NewPassphraseFile(t, home)
+	recoveryPhraseFilePath := NewFile(t, home, "recovery-phrase.txt", testRecoveryPhrase)
+	walletName := vgrand.RandomStr(5)
+
+	// when
+	importWalletResp, err := WalletImport(t, []string{
+		"--home", home,
+		"--output", "json",
+		"--wallet", walletName,
+		"--passphrase-file", passphraseFilePath,
+		"--recovery-phrase-file", recoveryPhraseFilePath,
+		"--version", "2",
+	})
+
+	// then
+	require.NoError(t, err)
+	AssertImportWallet(t, importWalletResp).
+		WithName(walletName).
+		LocatedUnder(home)
+
+	// when
+	signResp, err := SignCommand(t, []string{
+		"--home", home,
+		"--output", "json",
+		"--wallet", walletName,
+		"--pubkey", importWalletResp.Key.PublicKey,
+		"--passphrase-file", passphraseFilePath,
+		"--tx-height", "150",
+		`{"voteSubmission": {"proposalId": "some-id", "value": "VALUE_YES"}}`,
+	})
+
+	// then
+	require.NoError(t, err)
+	AssertSignCommand(t, signResp)
+}
+
+func TestSignCommandWithTaintedKey(t *testing.T) {
+	// given
+	home := t.TempDir()
+	_, passphraseFilePath := NewPassphraseFile(t, home)
+	recoveryPhraseFilePath := NewFile(t, home, "recovery-phrase.txt", testRecoveryPhrase)
+	walletName := vgrand.RandomStr(5)
+
+	// when
+	importWalletResp, err := WalletImport(t, []string{
+		"--home", home,
+		"--output", "json",
+		"--wallet", walletName,
+		"--passphrase-file", passphraseFilePath,
+		"--recovery-phrase-file", recoveryPhraseFilePath,
+		"--version", "2",
+	})
+
+	// then
+	require.NoError(t, err)
+	AssertImportWallet(t, importWalletResp).
+		WithName(walletName).
+		WithPublicKey("b5fd9d3c4ad553cb3196303b6e6df7f484cf7f5331a572a45031239fd71ad8a0").
+		LocatedUnder(home)
+
+	// when
+	err = KeyTaint(t, []string{
+		"--home", home,
+		"--output", "json",
+		"--wallet", walletName,
+		"--pubkey", importWalletResp.Key.PublicKey,
+		"--passphrase-file", passphraseFilePath,
+	})
+
+	// then
+	require.NoError(t, err)
+
+	// when
+	signResp, err := SignCommand(t, []string{
+		"--home", home,
+		"--output", "json",
+		"--wallet", walletName,
+		"--pubkey", importWalletResp.Key.PublicKey,
+		"--passphrase-file", passphraseFilePath,
+		"--tx-height", "150",
+		`{"voteSubmission": {"proposalId": "some-id", "value": "VALUE_YES"}}`,
+	})
+
+	// then
+	require.EqualError(t, err, "couldn't sign transaction: public key is tainted")
+	require.Nil(t, signResp)
+
+	// when
+	err = KeyUntaint(t, []string{
+		"--home", home,
+		"--output", "json",
+		"--wallet", walletName,
+		"--pubkey", importWalletResp.Key.PublicKey,
+		"--passphrase-file", passphraseFilePath,
+	})
+
+	// then
+	require.NoError(t, err)
+
+	// when
+	signResp, err = SignCommand(t, []string{
+		"--home", home,
+		"--output", "json",
+		"--wallet", walletName,
+		"--pubkey", importWalletResp.Key.PublicKey,
+		"--passphrase-file", passphraseFilePath,
+		"--tx-height", "150",
+		`{"voteSubmission": {"proposalId": "some-id", "value": "VALUE_YES"}}`,
+	})
+
+	// then
+	require.NoError(t, err)
+	AssertSignCommand(t, signResp)
+}
diff --git a/tests/sc_sign_and_verify_message_test.go b/tests/sc_sign_and_verify_message_test.go
index 1eca346..fbc3079 100644
--- a/tests/sc_sign_and_verify_message_test.go
+++ b/tests/sc_sign_and_verify_message_test.go
@@ -36,7 +36,7 @@ func TestSignMessage(t *testing.T) {
 	encodedMessage := base64.StdEncoding.EncodeToString(message)
 
 	// when
-	signResp, err := Sign(t, []string{
+	signResp, err := SignMessage(t, []string{
 		"--home", home,
 		"--output", "json",
 		"--wallet", walletName,
@@ -47,11 +47,11 @@ func TestSignMessage(t *testing.T) {
 
 	// then
 	require.NoError(t, err)
-	AssertSign(t, signResp).
+	AssertSignMessage(t, signResp).
 		WithSignature("StH82RHxjQ3yTeaSN25b6sJwAyLiq1CDvPWf0X4KIf/WTIjkunkWKn1Gq9ntCoGBfBZIyNfpPtGx0TSZsSrbCA==")
 
 	// when
-	verifyResp, err := Verify(t, []string{
+	verifyResp, err := VerifyMessage(t, []string{
 		"--home", home,
 		"--output", "json",
 		"--pubkey", importWalletResp.Key.PublicKey,
@@ -61,7 +61,7 @@ func TestSignMessage(t *testing.T) {
 
 	// then
 	require.NoError(t, err)
-	AssertVerify(t, verifyResp).IsValid()
+	AssertVerifyMessage(t, verifyResp).IsValid()
 }
 
 func TestSignMessageWithTaintedKey(t *testing.T) {
@@ -105,7 +105,7 @@ func TestSignMessageWithTaintedKey(t *testing.T) {
 	encodedMessage := base64.StdEncoding.EncodeToString(message)
 
 	// when
-	signResp, err := Sign(t, []string{
+	signResp, err := SignMessage(t, []string{
 		"--home", home,
 		"--output", "json",
 		"--wallet", walletName,
@@ -131,7 +131,7 @@ func TestSignMessageWithTaintedKey(t *testing.T) {
 	require.NoError(t, err)
 
 	// when
-	signResp, err = Sign(t, []string{
+	signResp, err = SignMessage(t, []string{
 		"--home", home,
 		"--output", "json",
 		"--wallet", walletName,
@@ -142,6 +142,6 @@ func TestSignMessageWithTaintedKey(t *testing.T) {
 
 	// then
 	require.NoError(t, err)
-	AssertSign(t, signResp).
+	AssertSignMessage(t, signResp).
 		WithSignature("StH82RHxjQ3yTeaSN25b6sJwAyLiq1CDvPWf0X4KIf/WTIjkunkWKn1Gq9ntCoGBfBZIyNfpPtGx0TSZsSrbCA==")
 }
diff --git a/version/version_test.go b/version/version_test.go
deleted file mode 100644
index 2c5ac3f..0000000
--- a/version/version_test.go
+++ /dev/null
@@ -1,162 +0,0 @@
-package version_test
-
-import (
-	"testing"
-
-	"code.vegaprotocol.io/vegawallet/version"
-	"github.com/blang/semver/v4"
-	"github.com/stretchr/testify/assert"
-	"github.com/stretchr/testify/require"
-)
-
-func TestCheckVersion(t *testing.T) {
-	t.Run("Checking current version succeeds", testCheckingCurrentVersionSucceeds)
-	t.Run("Verifying unreleased version succeeds", testVerifyingUnreleasedVersionSucceeds)
-}
-
-func testCheckingCurrentVersionSucceeds(t *testing.T) {
-	tcs := []struct {
-		name           string
-		currentVersion string
-		releases       []string
-		expectedResult *semver.Version
-	}{
-		{
-			name:           "stable release against older stable releases should not update",
-			currentVersion: "v0.4.0",
-			releases:       []string{"v0.2.0", "v0.3.0"},
-			expectedResult: nil, // means no update
-		}, {
-			name:           "stable release against newer stable releases should update to latest stable release",
-			currentVersion: "v0.1.0",
-			releases:       []string{"v0.2.0", "v0.3.0"},
-			expectedResult: toSemVer(t, "0.3.0"),
-		}, {
-			name:           "stable release against same stable release should not update",
-			currentVersion: "v0.3.0",
-			releases:       []string{"v0.2.0", "v0.3.0"},
-			expectedResult: nil, // means no update
-		}, {
-			name:           "stable against newer pre-release should update to latest stable release",
-			currentVersion: "v0.1.0",
-			releases:       []string{"v0.2.0", "v0.3.0-alpha"},
-			expectedResult: toSemVer(t, "0.2.0"),
-		}, {
-			name:           "stable release against older pre-release should not update",
-			currentVersion: "v0.3.0",
-			releases:       []string{"v0.2.0", "v0.3.0-alpha"},
-			expectedResult: nil, // means no update
-		}, {
-			name:           "pre-release against newer stable release should update to latest stable release",
-			currentVersion: "v0.3.0-alpha",
-			releases:       []string{"v0.2.0", "v0.3.0"},
-			expectedResult: toSemVer(t, "0.3.0"),
-		}, {
-			name:           "pre-release against older stable release should not update",
-			currentVersion: "v0.4.0-alpha",
-			releases:       []string{"v0.2.0", "v0.3.0"},
-			expectedResult: nil, // means no update
-		}, {
-			name:           "pre-release against newer pre-release should update to latest pre-release",
-			currentVersion: "v0.4.0-alpha",
-			releases:       []string{"v0.2.0", "v0.3.0", "v0.4.0-alpha", "v0.4.0-beta"},
-			expectedResult: toSemVer(t, "0.4.0-beta"),
-		}, {
-			name:           "pre-release against same pre-release should not update",
-			currentVersion: "v0.4.0-alpha",
-			releases:       []string{"v0.2.0", "v0.3.0", "v0.4.0-alpha"},
-			expectedResult: nil, // means no update
-		}, {
-			name:           "pre-release against newer pre-release separated by stable releases should update to latest stable release",
-			currentVersion: "v0.2.0-alpha",
-			releases:       []string{"v0.2.0-alpha", "v0.2.0", "v0.3.0-alpha", "v0.3.0", "v0.3.1", "v0.4.0-alpha"},
-			expectedResult: toSemVer(t, "0.3.1"),
-		}, {
-			name:           "pre-release against newer development pre-release should not update",
-			currentVersion: "v0.4.0-alpha",
-			releases:       []string{"v0.2.0", "v0.3.0", "v0.4.0-alpha+dev"},
-			expectedResult: nil, // means no update
-		}, {
-			name:           "development pre-release against non-development pre-release should update to non-development pre-release",
-			currentVersion: "v0.4.0-alpha+dev",
-			releases:       []string{"v0.2.0", "v0.3.0", "v0.4.0-alpha"},
-			expectedResult: toSemVer(t, "0.4.0-alpha"),
-		}, {
-			name:           "development pre-release against newer pre-release should update to latest pre-release",
-			currentVersion: "v0.4.0-alpha+dev",
-			releases:       []string{"v0.2.0", "v0.3.0", "v0.4.0-alpha", "v0.4.0-beta"},
-			expectedResult: toSemVer(t, "0.4.0-beta"),
-		},
-	}
-
-	for _, tc := range tcs {
-		t.Run(tc.name, func(tt *testing.T) {
-			// given
-			releasesGetter := func() ([]string, error) {
-				return tc.releases, nil
-			}
-
-			// when
-			newerVersion, err := version.Check(releasesGetter, tc.currentVersion)
-
-			// then
-			require.NoError(tt, err)
-			assert.Equal(tt, tc.expectedResult, newerVersion)
-		})
-	}
-}
-
-func testVerifyingUnreleasedVersionSucceeds(t *testing.T) {
-	tcs := []struct {
-		name         string
-		version      string
-		isUnreleased bool
-	}{
-		{
-			name:         "stable version is not unreleased",
-			version:      "0.1.0",
-			isUnreleased: false,
-		}, {
-			name:         "pre-release version is not unreleased",
-			version:      "0.1.0-pre",
-			isUnreleased: false,
-		}, {
-			name:         "development build on stable version is unreleased",
-			version:      "0.1.0+dev",
-			isUnreleased: true,
-		}, {
-			name:         "development pre-release version is unreleased",
-			version:      "0.1.0-alpha+dev",
-			isUnreleased: true,
-		}, {
-			name:         "annotated build on pre-release version is released",
-			version:      "0.1.0-alpha+12345",
-			isUnreleased: false,
-		}, {
-			name:         "non-semver version is unreleased",
-			version:      "test",
-			isUnreleased: true,
-		},
-	}
-
-	for _, tc := range tcs {
-		t.Run(tc.name, func(tt *testing.T) {
-			version.Version = tc.version
-
-			// when
-			isUnreleased := version.IsUnreleased()
-
-			// then
-			assert.Equal(tt, tc.isUnreleased, isUnreleased)
-		})
-	}
-}
-
-func toSemVer(t *testing.T, s string) *semver.Version {
-	t.Helper()
-	expectVersion, err := semver.New(s)
-	if err != nil {
-		t.Fatalf("couldn't parse the semver: %v", err)
-	}
-	return expectVersion
-}
diff --git a/wallet/handler_test.go b/wallet/handler_test.go
index a99c59b..18be825 100644
--- a/wallet/handler_test.go
+++ b/wallet/handler_test.go
@@ -6,7 +6,9 @@ import (
 	"sort"
 	"testing"
 
+	"code.vegaprotocol.io/protos/vega"
 	commandspb "code.vegaprotocol.io/protos/vega/commands/v1"
+	walletpb "code.vegaprotocol.io/protos/vega/wallet/v1"
 	vgrand "code.vegaprotocol.io/shared/libs/rand"
 	"code.vegaprotocol.io/vegawallet/wallet"
 	"code.vegaprotocol.io/vegawallet/wallet/mocks"
@@ -67,11 +69,11 @@ func testAnnotatingKeySucceeds(t *testing.T) {
 }
 
 func TestGenerateKey(t *testing.T) {
-	t.Run("Generating keys in non-existing wallet succeeds", testGenerateKeyInNonExistingWalletSucceeds)
+	t.Run("Generating keys in non-existing wallet fails", testGenerateKeyInNonExistingWalletFails)
 	t.Run("Generating keys in existing wallet succeeds", testGenerateKeyInExistingWalletSucceeds)
 }
 
-func testGenerateKeyInNonExistingWalletSucceeds(t *testing.T) {
+func testGenerateKeyInNonExistingWalletFails(t *testing.T) {
 	// given
 	req := &wallet.GenerateKeyRequest{
 		Wallet: vgrand.RandomStr(5),
@@ -83,36 +85,17 @@ func testGenerateKeyInNonExistingWalletSucceeds(t *testing.T) {
 	}
 
 	// setup
-	var generatedWallet wallet.Wallet
-	captureWallet := func(w wallet.Wallet, passphrase string) error {
-		generatedWallet = w
-		return nil
-	}
-	fakePath := fmt.Sprintf("/path/to/wallets/%s", req.Wallet)
 	store := handlerMocks(t)
 	store.EXPECT().WalletExists(req.Wallet).Times(1).Return(false)
-	store.EXPECT().GetWalletPath(req.Wallet).Times(1).Return(fakePath)
-	store.EXPECT().SaveWallet(gomock.Any(), req.Passphrase).Times(1).DoAndReturn(captureWallet)
+	store.EXPECT().GetWalletPath(req.Wallet).Times(0)
+	store.EXPECT().SaveWallet(gomock.Any(), req.Passphrase).Times(0)
 
 	// when
 	resp, err := wallet.GenerateKey(store, req)
 
 	// then
-	require.NoError(t, err)
-	require.NotNil(t, resp)
-	// verify generated wallet
-	assert.Equal(t, req.Wallet, generatedWallet.Name())
-	assert.Len(t, generatedWallet.ListKeyPairs(), 1)
-	keyPair := generatedWallet.ListKeyPairs()[0]
-	assert.Equal(t, req.Metadata, keyPair.Meta())
-	// verify response
-	assert.Equal(t, req.Wallet, resp.Wallet.Name)
-	assert.Equal(t, fakePath, resp.Wallet.FilePath)
-	assert.NotEmpty(t, resp.Wallet.Mnemonic)
-	assert.Equal(t, keyPair.PublicKey(), resp.Key.PublicKey)
-	assert.Equal(t, keyPair.AlgorithmName(), resp.Key.Algorithm.Name)
-	assert.Equal(t, keyPair.AlgorithmVersion(), resp.Key.Algorithm.Version)
-	assert.Equal(t, keyPair.Meta(), resp.Key.Meta)
+	require.ErrorIs(t, err, wallet.ErrWalletDoesNotExists)
+	require.Nil(t, resp)
 }
 
 func testGenerateKeyInExistingWalletSucceeds(t *testing.T) {
@@ -147,13 +130,10 @@ func testGenerateKeyInExistingWalletSucceeds(t *testing.T) {
 	keyPair := w.ListKeyPairs()[0]
 	assert.Equal(t, req.Metadata, keyPair.Meta())
 	// verify response
-	assert.Equal(t, req.Wallet, resp.Wallet.Name)
-	assert.Equal(t, fakePath, resp.Wallet.FilePath)
-	assert.Empty(t, resp.Wallet.Mnemonic)
-	assert.Equal(t, keyPair.PublicKey(), resp.Key.PublicKey)
-	assert.Equal(t, keyPair.AlgorithmName(), resp.Key.Algorithm.Name)
-	assert.Equal(t, keyPair.AlgorithmVersion(), resp.Key.Algorithm.Version)
-	assert.Equal(t, keyPair.Meta(), resp.Key.Meta)
+	assert.Equal(t, keyPair.PublicKey(), resp.PublicKey)
+	assert.Equal(t, keyPair.AlgorithmName(), resp.Algorithm.Name)
+	assert.Equal(t, keyPair.AlgorithmVersion(), resp.Algorithm.Version)
+	assert.Equal(t, keyPair.Meta(), resp.Meta)
 }
 
 func TestTaintKey(t *testing.T) {
@@ -541,6 +521,75 @@ func TestListWalletsSucceeds(t *testing.T) {
 	assert.Equal(t, expectedResp, resp)
 }
 
+func TestSignCommand(t *testing.T) {
+	t.Run("Sign message succeeds", testSignCommandSucceeds)
+	t.Run("Sign message of non-existing wallet fails", testSignCommandWithNonExistingWalletFails)
+}
+
+func testSignCommandSucceeds(t *testing.T) {
+	// given
+	w := importWalletWithKey(t)
+	kp := w.ListKeyPairs()[0]
+
+	req := &wallet.SignCommandRequest{
+		Wallet: w.Name(),
+		Request: &walletpb.SubmitTransactionRequest{
+			PubKey:    kp.PublicKey(),
+			Propagate: false,
+			Command: &walletpb.SubmitTransactionRequest_VoteSubmission{
+				VoteSubmission: &commandspb.VoteSubmission{
+					ProposalId: vgrand.RandomStr(5),
+					Value:      vega.Vote_VALUE_YES,
+				},
+			},
+		},
+		Passphrase: "passphrase",
+	}
+
+	// setup
+	store := handlerMocks(t)
+	store.EXPECT().WalletExists(req.Wallet).Times(1).Return(true)
+	store.EXPECT().GetWallet(req.Wallet, req.Passphrase).Times(1).Return(w, nil)
+
+	// when
+	resp, err := wallet.SignCommand(store, req)
+
+	// then
+	require.NoError(t, err)
+	require.NotNil(t, resp)
+	assert.NotEmpty(t, resp.Base64Transaction)
+}
+
+func testSignCommandWithNonExistingWalletFails(t *testing.T) {
+	// given
+	req := &wallet.SignCommandRequest{
+		Wallet: vgrand.RandomStr(5),
+		Request: &walletpb.SubmitTransactionRequest{
+			PubKey:    vgrand.RandomStr(5),
+			Propagate: false,
+			Command: &walletpb.SubmitTransactionRequest_VoteSubmission{
+				VoteSubmission: &commandspb.VoteSubmission{
+					ProposalId: vgrand.RandomStr(5),
+					Value:      vega.Vote_VALUE_YES,
+				},
+			},
+		},
+		Passphrase: "passphrase",
+	}
+
+	// setup
+	store := handlerMocks(t)
+	store.EXPECT().WalletExists(req.Wallet).Times(1).Return(false)
+	store.EXPECT().GetWallet(req.Wallet, req.Passphrase).Times(0)
+
+	// when
+	resp, err := wallet.SignCommand(store, req)
+
+	// then
+	require.Error(t, err)
+	assert.Nil(t, resp)
+}
+
 func TestSignMessage(t *testing.T) {
 	t.Run("Sign message succeeds", testSignMessageSucceeds)
 	t.Run("Sign message of non-existing wallet fails", testSignMessageWithNonExistingWalletFails)