Skip to content

Add Bankr signer support#16

Open
wtfsayo wants to merge 3 commits into
mainfrom
feat/bankr-signer-support
Open

Add Bankr signer support#16
wtfsayo wants to merge 3 commits into
mainfrom
feat/bankr-signer-support

Conversation

@wtfsayo
Copy link
Copy Markdown
Contributor

@wtfsayo wtfsayo commented May 20, 2026

Summary

  • add a Bankr Wallet API signer backend with /wallet/me address resolution, typed-data signing, personal message signing, signature normalization, and recovered-address verification
  • wire --bankr-signer default through CLI/runtime/auth/signer resolution and wallet address/show flows
  • keep raw Hyperliquid L1 action signing fail-closed for Bankr until Bankr exposes and documents compatible raw digest signing
  • add schema/catalog metadata, README guidance, a rollout plan, focused tests, and a live smoke example

Before / After

Before:

  • signer selection supported local keys, keystores, stored accounts, and OWS paths, but had no Bankr Wallet API integration
  • external managed signer coverage did not include Bankr address discovery, typed-data signing, or personal-sign verification
  • command schema metadata had no way to describe Bankr signer compatibility

After:

  • --bankr-signer default resolves the Bankr EVM wallet from BANKR_API_KEY and can participate in supported typed-data/message signing paths
  • Bankr signatures are normalized and rejected if recovered signer address does not match the resolved Bankr wallet
  • raw L1 action signing returns an explicit unsupported error instead of attempting unsafe or undocumented signing
  • schema fixtures/catalogs include bankr_signer metadata for agent-readable command compatibility

Verification

  • cargo fmt --check
  • cargo test --lib bankr -- --nocapture — 7 passed
  • git diff --check
  • cargo check -q

Live Smoke

  • Bankr wallet resolution succeeded with the provided key
  • raw L1 signing fail-closed locally before exchange submission
  • live Bankr signing was blocked by Bankr because the provided key is read-only: Read-only API key

Notes

  • The pasted Bankr API key should be rotated after testing.
  • PR branch has been rebased onto main and the PR base is now main.

Copilot AI review requested due to automatic review settings May 20, 2026 15:48
@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented May 20, 2026

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
❌ Deployment failed
View logs
hyperliquid-feedback 3d5c1eb May 20 2026, 07:31 PM

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds Bankr Wallet API as an additional signer backend in hyperliquid-cli, wiring it through CLI/runtime signer selection and exposing per-command Bankr capability metadata in the command registry/schema/catalog, along with documentation and contract tests.

Changes:

  • Introduces a new bankr signer backend with wallet address resolution, typed-data signing, message signing, signature normalization, and recovered-address verification.
  • Adds --bankr-signer to CLI/runtime resolution and threads Bankr selection through wallet commands and signer resolvers (while keeping raw Hyperliquid L1 signing fail-closed for Bankr).
  • Extends command metadata/schema/catalog + contract tests and documents Bankr signer usage and rollout plan.

Reviewed changes

Copilot reviewed 18 out of 19 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
src/bankr.rs New Bankr Wallet API integration (resolve address, sign typed data/messages, normalize + verify signatures).
src/signing.rs Adds SignerSource::Bankr / SignerBackend::Bankr and routes signing methods; raw L1 signing fails closed.
src/auth.rs Adds resolve_bankr_signer to produce a ResolvedSigner backed by Bankr.
src/resolvers.rs Extends signer resolver input with bankr_selector and resolves Bankr when explicitly selected.
src/main.rs Adds --bankr-signer global flag and conflict rules with other signer options.
src/cli_runtime.rs Propagates Bankr selector through runtime context, avoids private-key auto-resolution when Bankr is selected, and passes Bankr selector into wallet commands and signer resolution.
src/commands/wallet.rs Adds Bankr-aware wallet show/address resolution and display labeling.
src/command_registry.rs Adds BankrSupport enum + bankr_signer field to command contracts and catalog metadata plumbing.
src/command_metadata.rs Adds bankr_signer inference rules for per-command capability classification.
src/commands/schema.rs Emits bankr_signer in schema output surface.
src/command_catalog.json Materializes bankr_signer metadata for commands in the catalog.
tests/schema_contracts.rs Extends schema fixture assertions to include bankr_signer.
tests/registry_contracts.rs Extends registry/schema drift assertions to include bankr_signer.
tests/release_artifacts.rs Ensures tool catalog explicitly materializes bankr_signer metadata.
README.md Documents Bankr signer usage and reinforces fail-closed raw L1 limitation; updates safety rules.
docs/plans/bankr-signer-support.md Adds detailed Bankr signer rollout/implementation plan and constraints.
src/lib.rs Exposes the new bankr module.
Cargo.toml / Cargo.lock Enables reqwest blocking feature and updates lockfile for new dependency graph.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tests/schema_contracts.rs
Comment on lines 41 to 46
"dry_run": schema["dry_run"],
"raw_payload": schema["raw_payload"],
"confirmation": schema["confirmation"],
"ows_signer": schema["json_schema"]["x-hyperliquid"]["ows_signer"],
"bankr_signer": schema["json_schema"]["x-hyperliquid"]["bankr_signer"],
})
Comment on lines 70 to +77
assert_eq!(
command_value["ows_signer"], schema["json_schema"]["x-hyperliquid"]["ows_signer"],
"registry/schema drift for {command_key} field ows_signer"
);
assert_eq!(
command_value["bankr_signer"], schema["json_schema"]["x-hyperliquid"]["bankr_signer"],
"registry/schema drift for {command_key} field bankr_signer"
);
Comment thread src/command_catalog.json
Comment on lines 1267 to 1272
"dry_run": "not_supported",
"raw_payload": "unsupported",
"confirmation": "none",
"ows_signer": "address_selector_supported"
"ows_signer": "address_selector_supported",
"bankr_signer": "not_required"
},
Comment thread src/command_catalog.json
Comment on lines 2104 to 2109
"dry_run": "optional",
"raw_payload": "dry_run_only",
"confirmation": "prompt",
"ows_signer": "experimental_feature_gated"
"ows_signer": "experimental_feature_gated",
"bankr_signer": "not_required"
},
Comment thread src/command_catalog.json Outdated
"confirmation": "none",
"ows_signer": "local_only"
"ows_signer": "local_only",
"bankr_signer": "not_required"
Comment thread src/bankr.rs
Comment on lines +186 to +196
context: &'static str,
) -> Result<T, CliError> {
let url = bankr_url(api_base_url, path)?;
let response = reqwest::blocking::Client::builder()
.timeout(DEFAULT_TIMEOUT)
.build()
.map_err(|err| CliError::Internal(anyhow::anyhow!(err)))?
.get(url)
.header("X-API-Key", api_key)
.send()
.map_err(|err| CliError::Unavailable(format!("Check your network connection. {err}")))?;
Comment thread Cargo.toml Outdated
# Primary SDK for Hyperliquid API
hypersdk = "0.2"
reqwest = { version = "0.13", features = ["json"] }
reqwest = { version = "0.13", features = ["blocking", "json"] }
Comment thread src/bankr.rs Outdated
context: &'static str,
) -> Result<T, CliError> {
let url = bankr_url(api_base_url, path)?;
let response = reqwest::blocking::Client::builder()
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

WARNING: New reqwest::blocking::Client created per request — should be reused for connection pooling

Each call to get_json and post_json builds a new blocking HTTP client with its own connection pool that is immediately discarded. For a CLI making multiple Bankr API calls (resolve address + sign), this means no TLS session reuse and no connection pooling. Consider building the client once in resolve_selector and storing it in BankrSigningConfig, or lazily initializing a shared client.

Comment thread src/bankr.rs Outdated
}

fn is_hex_encoded_eip712_scalar(field_type: &str) -> bool {
field_type == "bytes"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

SUGGESTION: Redundant check — field_type == "bytes" is already covered by field_type.starts_with("bytes") on line 402

Suggested change
field_type == "bytes"
fn is_hex_encoded_eip712_scalar(field_type: &str) -> bool {
field_type.starts_with("bytes")
|| field_type.starts_with("uint")
|| field_type.starts_with("int")
}

Comment thread src/command_metadata.rs
"not_required".to_string()
}

fn inferred_bankr_signer(command_key: &str, command: &impl CatalogCommandMetadata) -> String {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

WARNING: inferred_bankr_signer is dead code — catalog JSON provides bankr_signer for all 112 commands

This function contains correct logic (e.g., returning unsupported_raw_l1_signing for auth-required commands, address_selector_supported for wallet show/address), but it is never invoked because every command in command_catalog.json already has an explicit bankr_signer: "not_required" value. Since all 112 catalog entries are "not_required", the nuanced inference logic is entirely unreachable.

This also means:

  • BankrSupport::TypedDataSupported, UnsupportedRawL1Signing, LocalOnly, NotApplicable, and AddressSelectorSupported variants are never assigned
  • Agents relying on schema metadata cannot determine which commands work with Bankr
  • The fail-closed metadata promise from the plan document is not delivered

Either remove the catalog overrides to let inference run, or update the catalog entries to use the correct values from the inference function.

@kilo-code-bot
Copy link
Copy Markdown

kilo-code-bot Bot commented May 20, 2026

Code Review Summary

Status: 5 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 1
WARNING 3
SUGGESTION 1
Issue Details (click to expand)

CRITICAL

File Line Issue
src/command_catalog.json all All 112 commands have bankr_signer: "not_required" — auth-required commands like orders create and transfer send should be unsupported_raw_l1_signing, wallet show/wallet address should be address_selector_supported, local-only commands should be not_applicable

WARNING

File Line Issue
src/bankr.rs 562 parse_bankr_integer_scalar returns Option<u64> — cannot represent uint256 values above 2^64-1; silently leaves large values as strings instead of normalizing
src/command_metadata.rs 186 inferred_bankr_signer is dead code — catalog JSON overrides all inference with "not_required"
src/command_catalog.json fixture registry_inventory.json and schema_high_risk_metadata.json fixtures all have "bankr_signer": null / "not_required" — propagating the stale catalog metadata into contract tests

SUGGESTION

File Line Issue
src/bankr.rs 474 Redundant field_type == "bytes" check — already covered by .starts_with("bytes") on next line
Other Observations (not in diff)

Issues found in unchanged code or spanning many lines that cannot receive inline comments:

File Line Issue
src/command_registry.rs 275-282 BankrSupport enum has 5 variants that are never assigned (AddressSelectorSupported, TypedDataSupported, UnsupportedRawL1Signing, LocalOnly, NotApplicable) because all catalog entries resolve to NotRequired
src/bankr.rs 310-314 HTTP 400 mapped to InvalidAuth (exit code 10) — a 400 could also indicate a malformed payload, not just an auth problem. Currently harmless but may mislead agents interpreting the exit code
Changes Since Last Review

New commit fedb003 — "Fix Bankr HTTP integration for async CLI and API payload shape."

  • Resolved: Blocking client per request → replaced with OnceLock-based singleton async reqwest::Client with block_in_place bridge
  • Resolved: Cargo.toml blocking feature removed — no longer needed
  • Resolved: Previous runtime-blocking concern resolved — run_bankr_async uses block_in_place inside multi-threaded runtime (CLI uses #[tokio::main])
  • Unchanged: Catalog CRITICAL — all 112 commands still "not_required"
  • Unchanged: Dead code (inferred_bankr_signer) — still present
  • Unchanged: Redundant bytes check — still present
  • New: Integer normalization for Bankr API — added normalize_bankr_numeric_fields to convert hex/decimal string values to JSON numbers
  • New: Better error handling — 400 responses and success:false bodies now mapped to explicit error variants
  • New: parse_bankr_integer_scalar u64 ceiling — see WARNING above
Files Reviewed (21 files)
  • Cargo.lock - 0 issues (generated)
  • Cargo.toml - 0 issues (blocking feature removed ✅)
  • README.md - 0 issues
  • docs/plans/bankr-signer-support.md - 0 issues (plan doc)
  • examples/bankr_live_smoke.rs - 0 issues (manual smoke test)
  • src/auth.rs - 0 issues
  • src/bankr.rs - 1 issue (u64 truncation in integer normalization)
  • src/cli_runtime.rs - 0 issues
  • src/command_catalog.json - 1 issue (all commands not_required)
  • src/command_metadata.rs - 1 issue (dead code)
  • src/command_registry.rs - 0 issues (unused variants noted in observations)
  • src/commands/schema.rs - 0 issues
  • src/commands/wallet.rs - 0 issues
  • src/lib.rs - 0 issues
  • src/main.rs - 0 issues
  • src/resolvers.rs - 0 issues
  • src/signing.rs - 0 issues
  • tests/registry_contracts.rs - 0 issues
  • tests/release_artifacts.rs - 0 issues
  • tests/schema_contracts.rs - 0 issues
  • tests/fixtures/contracts/* - 0 issues (generated snapshots)

Key Takeaway: The most important issue remains the catalog metadata. The inferred_bankr_signer function has correct logic for classifying Bankr support per command, but every command in the catalog overrides it with "not_required". The new async HTTP integration and OnceLock client are well-implemented. The new integer normalization is a good addition but has a u64 ceiling that could silently fail for large uint256 values.

Fix these issues in Kilo Cloud


Reviewed by glm-5.1 · Incremental review from commit 4884e40fedb003


Reviewed by glm-5.1 · 303,868 tokens

@wtfsayo wtfsayo force-pushed the feat/bankr-signer-support branch from c45d0b7 to ec75d36 Compare May 20, 2026 18:24
@wtfsayo wtfsayo changed the base branch from develop to main May 20, 2026 18:25
@wtfsayo wtfsayo force-pushed the feat/bankr-signer-support branch from 7d80fe0 to 4884e40 Compare May 20, 2026 18:30
Use async reqwest with block_in_place so --bankr-signer works inside the
Tokio runtime, normalize EIP-712 integers for Bankr validation, and surface
sign/400 failures with clearer auth errors until live signing is unblocked.

Co-authored-by: Cursor <cursoragent@cursor.com>
Comment thread src/bankr.rs
}
}

fn parse_bankr_integer_scalar(text: &str) -> Option<u64> {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

WARNING: parse_bankr_integer_scalar returns Option<u64>, which cannot represent values above 2^64 - 1. EIP-712 types like uint256 can hold values up to 2^256 - 1. When a hex/decimal string exceeds u64::MAX, the parse silently fails (returns None), leaving the field as a string instead of a JSON number. If Bankr's API requires numeric JSON values for integer fields, large values will cause a runtime error downstream.

For Hyperliquid actions specifically, nonce fields (millisecond timestamps ~1.8×10¹²) fit in u64, but this function is called generically for all uint*/int* EIP-712 fields including amount and size which could theoretically exceed u64 in edge cases. Consider using a big-integer parser or at minimum documenting the u64 ceiling.

Classify bankr_signer per command (unsupported_raw_l1_signing for auth-required mutating commands, address_selector_supported for wallet show/address, local_only for prio bid, not_applicable for local-only) so registry inference is not overridden incorrectly. Regenerate characterization fixtures and drop redundant bytes type check.

Co-authored-by: Cursor <cursoragent@cursor.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.

2 participants