Skip to content

feat(wallets): Turnkey wallet provider (TS 0.5.1 / Py 0.4.3) - #72

Open
jardenx wants to merge 7 commits into
mainfrom
feat/turnkey-wallet-signed
Open

feat(wallets): Turnkey wallet provider (TS 0.5.1 / Py 0.4.3)#72
jardenx wants to merge 7 commits into
mainfrom
feat/turnkey-wallet-signed

Conversation

@jardenx

@jardenx jardenx commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds Turnkey as a wallet provider (TS + Python): remote enclave signer over Turnkey's API — signMessage / signTransaction / signTypedData, capabilities auto-derived, default LocalExecutor, x402 support via X402Signer for free. Configured via env (TURNKEY_API_PUBLIC_KEY / TURNKEY_API_PRIVATE_KEY / TURNKEY_ORG_ID / TURNKEY_SIGN_WITH, optional TURNKEY_API_BASE_URL) and wired into ERC-8183 config via WALLET_KIND=turnkey.

Based on the 2026-07 POC (poc-space/turnkey/): 11 probes PASS, ERC-8004 on-chain registration, 8183 quote, x402 buyer/seller loops, non-root policy enforcement all verified live.

TypeScript (@bnbagent/sdk 0.5.0 → 0.5.1)

  • src/wallets/turnkey/TurnkeyWalletProvider with lazy-loaded optional peers @turnkey/sdk-server + @turnkey/viem (altana-style sdkLoader seam, published .d.ts stays free of @turnkey/* references).
  • EIP712Domain strip-then-inject fix: @turnkey/viem ≤0.14.34 silently serializes the domain as {} when EIP712Domain isn't explicit in types (signature succeeds, gets billed, binds an empty domain). We strip on input, inject getTypesForEIP712Domain({domain}) before handing to the vendor account, and compute messageHash from the un-injected form. Injection is idempotent if upstream fixes it.
  • expectedChainId fail-closed (rejected before any billable call); legacy + EIP-1559 both supported via viem type inference; strict SigningPolicy enforced before signing.
  • Vendor error mapping: 429 → 1 RPS hint, quota → 25 sigs/month billing hint, policy rejection → ALLOW-policy guidance.
  • erc8183/config.ts: WALLET_KIND=turnkey branch with optional walletAddress checksum reconciliation.

Python (bnbagent 0.4.2 → 0.4.3)

  • bnbagent/wallets/turnkey/ — self-built P-256 stamper + minimal activity client (only sign_raw_payload / sign_transaction; Turnkey ships no full Python SDK). cryptography as optional extra bnbagent[turnkey].
  • Same public surface as TS; deliberate implementation differences: constructor-level immunity to the EIP712Domain pitfall (pinned by test), explicit accessList rejection, 1559 v normalized from y-parity.
  • Factory + AgentConfig wallet_kind="turnkey" support, aligned with TS erc8183/config.ts.

Tests / E2E

  • CI is fully mocked — zero Turnkey quota consumed. Mocked accounts wrap real keys so signatures are recoverable and domain binding is provable.
  • Type-compat tests pin mirror types against real @turnkey/* devDeps (upstream drift = typecheck failure).
  • Real E2E is env-gated only: TURNKEY_E2E=1 pnpm -C typescript e2e:turnkey (5 sigs) + python examples/turnkey_e2e.py (4 sigs). Both ran PASS on BSC testnet (9 billable sigs total).

Verification

  • pnpm -C typescript check green (1151 tests + 1 skip; dist/**/*.d.ts has no @turnkey references)
  • cd python && uv run pytest green (820 tests)
  • Real E2E on BSC testnet: TS 5/5 + Python 4/4 PASS

Release notes

  • Merge order: this PR → publish npm @bnbagent/sdk@0.5.1 + PyPI bnbagent==0.4.3 → studio feat/turnkey-wallet PR (studio pins the published version; its lockfile can only regenerate after 0.5.1 is on npm).
  • Known constraints (documented, out of scope): multi-struct EIP-712 (Permit2) rejected at SDK level; ethSendTransaction managed broadcast never used (paywall); root API keys bypass all server-side Turnkey policies — production posture (non-root user + explicit ALLOW policies) called out in docs.

Pure signer over Turnkey's hosted API (keys in AWS Nitro enclaves): three sign.* overrides + CALLS_ARBITRARY/PAYMASTER_SPONSOR, lazy optional-peer loading (@turnkey/sdk-server + @turnkey/viem), strictDefault SigningPolicy enforced before every billable call, explicit EIP712Domain injection (0.14.x stripping trap), expectedChainId fail-closed pin.
Config kind dispatch gains a turnkey branch: TurnkeyWalletProvider.fromEnv pinned to the network's chain id, optional WALLET_ADDRESS anchor checked fail-closed (WalletIdentityMismatch). WALLET_PASSWORD stays EVM-only.
publicApi asserts the Tier-1/Tier-2 turnkey surface; turnkeyTypeCompat pins the structural mirrors against @turnkey/sdk-server@8.1.0 and @turnkey/viem@0.14.34 at typecheck time.
…ity client

Turnkey ships no Python SDK, so this adds the thin slice the provider needs: P-256 X-Stamp request stamping (cryptography, new optional extra bnbagent[turnkey]), a minimal SIGN_RAW_PAYLOAD_V2/SIGN_TRANSACTION_V2 activity client over requests, and a pure-signer provider (191 blind digest, 712 full-document payload with EIP712Domain always included, legacy+1559 unsigned RLP). Registered as factory kind 'turnkey'; conformance matrix extended; fake in-process enclave (eth_account) keeps CI offline.
… 0.5.0

Env-gated live E2Es (TS 5 / Py 4 billed signatures, serial >=1.1s, chain-id assert before anything billable), wallet-matrix rows in the three READMEs with the root-key and per-signature-billing warnings.
@jardenx jardenx changed the title feat(wallets): Turnkey wallet provider (TS 0.5.1 / Py 0.5.0) feat(wallets): Turnkey wallet provider (TS 0.5.1 / Py 0.4.3) Aug 10, 2026
@hashdit-bot

hashdit-bot Bot commented Aug 11, 2026

Copy link
Copy Markdown

Pull Request Review

This PR adds Turnkey remote-enclave wallet providers to the Python and TypeScript SDKs, including EIP-191, EIP-712, legacy, and EIP-1559 signing, environment-based configuration, ERC-8183 integration, and optional dependencies. It also adds lazy SDK loading, P-256 request stamping for Python, EIP-712 domain handling, extensive mocked tests, live E2E examples, documentation, and package version bumps.

Sensitive Content

Private Key / Seed Phrase / Mnemonic / Secret Material:

  • 0xc3...c3c3 (deterministic test private key) in python/tests/test_turnkey_provider.py — used by the fake Turnkey enclave for recoverable test signatures
  • 0xc3...c3c3 (deterministic test private key) in typescript/tests/turnkeyProvider.test.ts — used by the mocked Turnkey account for recoverable test signatures

Security Issues

No serious security issues detected.


Generated by Hashdit Bot. This tool can absolutely NOT replace manual audits.

@hashdit-bot

hashdit-bot Bot commented Aug 14, 2026

Copy link
Copy Markdown

Pull Request Review

This PR adds Turnkey remote-enclave wallet providers to the Python and TypeScript SDKs, including message, transaction, and EIP-712 signing, wallet factory/config integration, lazy optional dependencies, chain-ID pinning, and signing-policy enforcement. It also adds EIP-712 domain-injection safeguards, extensive mocked and live E2E coverage, documentation, dependency updates, and version bumps to Python 0.4.3 and TypeScript 0.5.1.

Sensitive Content

No sensitive content detected.

Security Issues

No serious security issues detected.


Generated by Hashdit Bot. This tool can absolutely NOT replace manual audits.

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