Skip to content

Add @walletconnect/staking-cli package and CLI SDK improvements#2

Merged
arein merged 1 commit intomainfrom
feat/staking-cli
Feb 13, 2026
Merged

Add @walletconnect/staking-cli package and CLI SDK improvements#2
arein merged 1 commit intomainfrom
feat/staking-cli

Conversation

@arein
Copy link
Member

@arein arein commented Feb 13, 2026

New package: @walletconnect/staking-cli

  • CLI for WCT staking on Optimism (chain ID 10)
  • Commands: stake, unstake, claim, status, balance
  • Smart stake flow: reads on-chain position first, picks correct contract call (createLock/updateLock/increaseLockAmount)
  • Gas estimation with 20% buffer, tx receipt polling after approve
  • Foundation API integration for position/rewards/APY data
  • 20 unit tests covering contracts and formatting

CLI SDK enhancements:

  • Global config: walletconnect config set/get project-id (persisted to ~/.walletconnect-cli/config.json)
  • Transaction decode: runs cast 4byte-decode on calldata when cast CLI is available, shows decoded function + args
  • Wallet name: logs which wallet is being prompted on every request
  • Process exit: both CLIs call process.exit(0) on success to avoid hanging from SignClient internal timers
  • Relay cleanup: destroy() closes WebSocket transport, disconnect() absorbs relay errors to always clean up local state

Skills:

  • walletconnect skill for wallet connection/signing operations
  • walletconnect-staking skill for staking operations

New package: @walletconnect/staking-cli
- CLI for WCT staking on Optimism (chain ID 10)
- Commands: stake, unstake, claim, status, balance
- Smart stake flow: reads on-chain position first, picks correct
  contract call (createLock/updateLock/increaseLockAmount)
- Gas estimation with 20% buffer, tx receipt polling after approve
- Foundation API integration for position/rewards/APY data
- 20 unit tests covering contracts and formatting

CLI SDK enhancements:
- Global config: walletconnect config set/get project-id
  (persisted to ~/.walletconnect-cli/config.json)
- Transaction decode: runs cast 4byte-decode on calldata when
  cast CLI is available, shows decoded function + args
- Wallet name: logs which wallet is being prompted on every request
- Process exit: both CLIs call process.exit(0) on success to avoid
  hanging from SignClient internal timers
- Relay cleanup: destroy() closes WebSocket transport, disconnect()
  absorbs relay errors to always clean up local state

Skills:
- walletconnect skill for wallet connection/signing operations
- walletconnect-staking skill for staking operations

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@arein arein merged commit 5b3a76c into main Feb 13, 2026
1 check passed
const data = params[0]?.data;
if (data && data !== "0x") {
try {
const decoded = execSync(`cast 4d ${data}`, {

Check warning

Code scanning / CodeQL

Unsafe shell command constructed from library input Medium

This string concatenation which depends on
library input
is later used in a
shell command
.

Copilot Autofix

AI 27 days ago

In general, to fix this kind of issue you should avoid constructing shell commands by concatenating untrusted strings and passing them to an API that invokes a shell (exec/execSync with a single string). Instead, either (1) pass arguments as an array to a non-shell-spawning API such as execFile/execFileSync, or (2) if you must invoke a shell, properly escape any untrusted values using a library like shell-quote before embedding them in the command string.

The best fix here, without changing functionality, is to keep calling the cast CLI but pass it as a program with separate arguments, so that the untrusted data value is not interpreted by the shell. We can do this by importing execFileSync from child_process alongside execSync (leaving execSync untouched if used elsewhere) and replacing the unsafe execSync("cast 4d " + data, ...) call with execFileSync("cast", ["4d", data], ...). execFileSync does not invoke a shell and treats data as a literal argument to cast, eliminating command injection while preserving behavior (same program, same args, same options).

Concretely:

  • Update the import on line 2 in packages/cli-sdk/src/client.ts to also import execFileSync.
  • In logRequestDetails, replace the execSync invocation on lines 234–238 with an execFileSync call that passes "cast" as the command and ["4d", data] as the arguments, using the same options (encoding, timeout, stdio).

No additional helper methods are needed; only this import and call-site replacement are required.

Suggested changeset 1
packages/cli-sdk/src/client.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/packages/cli-sdk/src/client.ts b/packages/cli-sdk/src/client.ts
--- a/packages/cli-sdk/src/client.ts
+++ b/packages/cli-sdk/src/client.ts
@@ -1,5 +1,5 @@
 import { EventEmitter } from "events";
-import { execSync } from "child_process";
+import { execSync, execFileSync } from "child_process";
 import { homedir } from "os";
 import { join } from "path";
 import { KeyValueStorage } from "@walletconnect/keyvaluestorage";
@@ -231,7 +231,7 @@
       const data = params[0]?.data;
       if (data && data !== "0x") {
         try {
-          const decoded = execSync(`cast 4d ${data}`, {
+          const decoded = execFileSync("cast", ["4d", data], {
             encoding: "utf-8",
             timeout: 5000,
             stdio: ["pipe", "pipe", "pipe"],
EOF
@@ -1,5 +1,5 @@
import { EventEmitter } from "events";
import { execSync } from "child_process";
import { execSync, execFileSync } from "child_process";
import { homedir } from "os";
import { join } from "path";
import { KeyValueStorage } from "@walletconnect/keyvaluestorage";
@@ -231,7 +231,7 @@
const data = params[0]?.data;
if (data && data !== "0x") {
try {
const decoded = execSync(`cast 4d ${data}`, {
const decoded = execFileSync("cast", ["4d", data], {
encoding: "utf-8",
timeout: 5000,
stdio: ["pipe", "pipe", "pipe"],
Copilot is powered by AI and may make mistakes. Always verify output.
arein added a commit that referenced this pull request Mar 10, 2026
Resolves CodeQL alert #2 (actions/missing-workflow-permissions) by
setting contents: read — the minimum permission needed for checkout.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
arein added a commit that referenced this pull request Mar 10, 2026
Resolves CodeQL alert #2 (actions/missing-workflow-permissions) by
setting contents: read — the minimum permission needed for checkout.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
arein added a commit that referenced this pull request Mar 10, 2026
Resolves CodeQL alert #2 (actions/missing-workflow-permissions) by
setting contents: read — the minimum permission needed for checkout.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant