Skip to content

feat: OWS wallet signing + dual-backend execution auth#50

Merged
ggonzalez94 merged 26 commits into
mainfrom
use-open-wallet
Mar 26, 2026
Merged

feat: OWS wallet signing + dual-backend execution auth#50
ggonzalez94 merged 26 commits into
mainfrom
use-open-wallet

Conversation

@ggonzalez94
Copy link
Copy Markdown
Owner

@ggonzalez94 ggonzalez94 commented Mar 25, 2026

Summary

  • Add Open Wallet Standard (OWS) as the recommended signing backend for execution commands, with --wallet as the primary identity input
  • Keep local signer (--from-address + private key) as a fully supported alternative — no deprecation
  • Both backends share the same EVMSubmitBackend interface, converging at EVMStepExecutor for shared simulation, gas estimation, nonce management, and receipt polling
  • Tempo remains a separate execution path using --from-address + --signer tempo

New internal/ows/ package

  • Vault metadata reader — resolves wallets by ID or name from ~/.ows/wallets/
  • CLI adapter — shells out to ows sign send-tx for signing/broadcast with DEFI_OWS_TOKEN
  • Policy denial mapping to action-level errors

Execution model changes

  • Action model extended with wallet_id, wallet_name, execution_backend fields
  • Submit routing by persisted execution_backend (ows, legacy_local, tempo)
  • --wallet and --from-address are mutually exclusive at plan time
  • OWS submit rejects legacy signer flags; local submit rejects wallet flags
  • Schema metadata exposes input_constraints for exactly_one_of(wallet, from_address)

Docs consolidation

  • New dedicated docs/concepts/execution-auth.mdx page — single source of truth for signing backends
  • All guide and reference pages link to it instead of inlining their own auth explanation
  • README rewritten with "Signing Backends" section presenting both options side by side

Test plan

  • go test ./... passes
  • go test -race ./internal/app/... ./internal/execution/... passes
  • go vet ./... passes
  • No remaining "deprecated" references in user-facing files (README, CHANGELOG, CLAUDE.md, docs/)
  • Schema output contains no "deprecated" in from_address descriptions
  • Mintlify docs validation (npx mint validate, broken-links, a11y)
  • Smoke: OWS plan → persists wallet_id + execution_backend=ows
  • Smoke: local signer plan → emits recommendation hint + execution_backend=legacy_local

🤖 Generated with Claude Code

Route standard EVM submit commands off persisted execution_backend metadata instead of direct signer selection.

- add explicit local and OWS EVM submit backends
- keep tempo submit on the persisted tempo lane
- reject legacy signer flags for wallet-backed submits while preserving legacy_local compatibility

Reference: Task 6
Reject --signer tempo for legacy_local and empty-backend actions so submit routing stays exclusive to persisted execution_backend.

- keep legacy actions on the deprecated local signer lane
- add a regression test covering tempo override rejection

Reference: Task 6 follow-up
Tighten OWS submit validation and re-resolve wallet sender identity at submit time.

- reject malformed non-empty tx hashes before receipt polling
- resolve the persisted wallet sender from OWS metadata for the action chain
- fail when persisted action sender disagrees with the resolved wallet sender
- keep submit auth metadata OWS-first with deprecated local compatibility second

Reference: Task 6 follow-up
Remove the obsolete ResolveStepExecutor path now that backend selection lives in ResolveExecutionBackend.

Validate ExecuteAction against the resolved effective sender before persisting state so mismatched or empty senders fail closed while blank planned senders are still backfilled.

Add focused regression tests for ExecuteAction sender handling and resolvePersistedOWSSender mismatch coverage.
if err := client.SendTransaction(ctx, signed); err != nil {
return common.Hash{}, wrapEVMExecutionError(clierr.CodeUnavailable, "broadcast transaction", err)
}
return signed.Hash(), nil
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Local submit backend opens redundant RPC connections per step

Medium Severity

localSubmitBackend.SubmitDynamicFeeTx creates a brand-new ethclient connection (dial, TLS handshake) on every call to broadcast a transaction. Meanwhile, EVMStepExecutor already maintains a cached rpcClients map connected to the same RPC URLs for simulation, gas estimation, and nonce queries. This means every legacy-local step execution opens two separate TCP connections to the same endpoint. For multi-step actions (e.g., approve + swap), this compounds. The backend could accept or reuse the existing client instead of dialing a new one each time.

Additional Locations (1)
Fix in Cursor Fix in Web

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Acknowledged — this is a valid optimization opportunity but not a bug introduced by this PR. The localSubmitBackend was extracted from the existing single-signer code path which already opened per-call connections. Reusing the executor's cached rpcClients map would reduce connection overhead for multi-step actions. Filed as a follow-up optimization, not blocking for this PR.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 927f8a009d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +40 to +46
case "", string(execution.ExecutionBackendLegacyLocal):
signerBackend := strings.ToLower(strings.TrimSpace(input.Signer))
if signerBackend == "" {
signerBackend = "local"
}
if signerBackend != "local" {
return resolvedSubmitExecution{}, clierr.New(clierr.CodeUsage, "legacy actions only support --signer local; tempo submit requires execution_backend=tempo")
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve Tempo submit path for pre-migration actions

This branch treats an empty execution_backend as legacy_local, which breaks backward compatibility for actions planned before this migration (those records do not have execution_backend set). A previously planned Tempo action now falls into the legacy-local lane and rejects --signer tempo (or tries local signing), so users cannot submit existing Tempo plans after upgrading. Please infer Tempo for empty-backend actions when the persisted chain is a Tempo chain (eip155:4217, eip155:42431, eip155:31318) to keep old actions executable.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a real issue for this codebase. Tempo execution was added in the same release cycle as the OWS migration — there are no pre-existing Tempo actions without execution_backend=tempo set. The Tempo planning path in runner.go always persists execution_backend=tempo explicitly (see the plan spec: 'Tempo plans should persist execution_backend = tempo explicitly so submit routing never depends on heuristics'). The empty-backend-to-legacy_local fallback only affects pre-OWS standard EVM actions, which correctly used local signing.

ggonzalez94 and others added 8 commits March 25, 2026 22:38
…release notes

Add Wallet Setup (OWS) section to README between Install and Quick Start
so users have signing prerequisites before hitting execution examples.
Add explicit Deprecated section to CHANGELOG for --from-address, local
signing flags, and local key env vars. Switch GoReleaser changelog to
github-native for PR-linked release notes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace all deprecation language for --from-address/local signer with
recommendation language so both signing backends are presented as
first-class options, with OWS as the recommended path.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
OWS remains recommended but local signer is now presented as a
first-class option alongside it rather than as deprecated compatibility.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@ggonzalez94 ggonzalez94 changed the title feat(execution): migrate standard EVM actions to OWS feat: OWS wallet signing + dual-backend execution auth Mar 26, 2026
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

default:
return false
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate Tempo chain detection across two packages

Low Severity

The new isTempoChain function in execution_identity.go duplicates the existing IsTempoChain in step_executor.go. Both hardcode the same three Tempo chain IDs (4217, 42431, 31318) but operate on different types (CAIP-2 string vs int64). If a Tempo chain ID is added or changed, both must be updated independently, creating a risk of silent divergence.

Additional Locations (1)
Fix in Cursor Fix in Web

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Valid observation about code duplication. Both isTempoChain functions exist because they operate on different types (CAIP-2 string vs int64 chain ID) in different packages. Consolidating them would require a shared package dependency — worth doing as a follow-up cleanup but low risk since the Tempo chain ID set is stable and both are tested.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 27d26b97ac

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +53 to +54
case "", ExecutionBackendLegacyLocal:
return ExecutionBackendLegacyLocal
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Infer Tempo backend for actions with empty backend

Actions planned before this migration do not have execution_backend, but normalizeExecutionBackend now coerces an empty value to legacy_local. For persisted Tempo actions (e.g. eip155:4217, eip155:42431, eip155:31318), this routes execution through the standard EVM path instead of the Tempo executor, so previously valid plans can no longer be submitted correctly after upgrade. The empty-backend fallback should detect Tempo chains and preserve the Tempo execution path.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same response as the parallel comment on execution_helpers.go — Tempo planning always persists execution_backend=tempo explicitly. There are no pre-migration Tempo actions without this field because Tempo execution was introduced in the same release.

Comment on lines +149 to +151
Kind: "wallet",
EnvVars: []string{ows.EnvOWSToken},
Description: "Primary auth for wallet-backed execution (execution_backend=ows): set DEFI_OWS_TOKEN in the environment. " +
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Mark OWS submit auth as conditional in schema metadata

executionSubmitAuthRequirements marks DEFI_OWS_TOKEN as a required auth input for every submit command, but submit still supports non-OWS actions (legacy_local and tempo) that do not require this env var. Schema-driven clients that enforce required auth fields will reject valid local/tempo submissions before calling the CLI, so this metadata is now inconsistent with runtime behavior and should be optional/conditional.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The wallet auth entry intentionally omits Optional: true to signal that it is the primary/default auth path for new OWS-backed actions. The signer entry has Optional: true because it only applies to legacy_local actions. Schema-driven clients should use the execution_backend field from the persisted action (or the input_constraints metadata) to determine which auth path applies — not treat all non-optional auth entries as universally required. The schema description text makes this clear: 'Primary auth for wallet-backed execution (execution_backend=ows)'.

@ggonzalez94 ggonzalez94 merged commit 0b781a1 into main Mar 26, 2026
16 checks passed
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