feat: Privy server wallet integration (policies-ready, official SDK)#649
Draft
fengtality wants to merge 6 commits into
Draft
feat: Privy server wallet integration (policies-ready, official SDK)#649fengtality wants to merge 6 commits into
fengtality wants to merge 6 commits into
Conversation
…ware registration Productionizes the Privy wallet integration from PR #618, dropping the execute-tx endpoints and rebuilding the client layer on the official SDK: - Replace hand-rolled REST client (deprecated auth.privy.io endpoint, Basic auth only) with PrivyService wrapping @privy-io/node, loaded lazily so its ESM-only deps don't affect startup or Jest - Support authorization keys (apiKeys.privyAuthorizationKey): requests are P-256 signed when wallets/policies have an owner, so the app secret alone cannot bypass policies - Sign-only flow: Gateway builds and broadcasts transactions on its own RPC connections; Privy only signs (no caip2/network coupling) - Round-trip verification in both signers: Solana compares signed message bytes and checks the wallet's signature; Ethereum parses the returned RLP and verifies from/to/value/data/nonce/chainId before broadcast - add-privy response surfaces policyIds/hasOwner and warns when a wallet has no policy (can sign anything) or no owner (app secret can remove policies) - GET /wallet now lists privyWalletAddresses alongside hardware wallets - Read credentials at call time so runtime config updates take effect; sanitize Privy error bodies out of API responses (full detail at debug) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…n endpoint Connector integration (parity with hardware wallets, now unified): - Solana chain class gains getWalletType/prepareWallet/signTransactionByType; Raydium, PancakeSwap-Sol, Jupiter, and wrap/unwrap routes route signing by wallet type (local keypair / Ledger / Privy) instead of a hardware boolean - PancakeSwap-Sol CLMM routes were local-only; they now support hardware and Privy wallets via the chain-level helpers - Ethereum chain class gains getWalletType/getSigner; all EVM write paths (approve, wrap, unwrap, Uniswap/PancakeSwap/0x execute and liquidity routes) obtain their signer via getSigner so Privy wallets ride the regular ethers Contract flow through PrivyEvmSigner (now exposing .address for Wallet drop-in compatibility) - Balance and allowance reads use by-address paths for Privy wallets (no local key file to load) - Jupiter buildSwapTransactionForHardwareWallet renamed to buildUnsignedSwapTransaction (it serves any externally-signed wallet) Policy lifecycle and hardening: - POST /wallet/privy-policy creates a Privy policy (EVM to-address allowlist / Solana program-ID allowlist) owned by the configured authorization key, and optionally attaches it to a wallet - PrivyService derives the P-256 public key from privyAuthorizationKey to set policy ownership; without the key the response warns that the app secret alone can modify the policy - add-privy/remove-privy now require the gateway passphrase, mirroring show-private-key - scripts/test-privy-live.js: live credential check, wallet inspection, and optional sign test against a policy-restricted wallet Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Addresses the js/missing-rate-limiting alert on POST /wallet/privy-policy: - Explicit per-route rate limit (10/min) on top of the global limiter - Require the gateway passphrase, consistent with add-privy/remove-privy (creating or attaching a policy changes a wallet's signing scope) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
axios isn't a dependency; Node 22+ fetch is. Privy's GET /v1/wallets returns HTTP 500 when passed a bare limit query param - call it plain. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Verified live: policy denials return HTTP 400 with body
{"error":"RPC request denied due to policy violation","code":"policy_violation"},
and signing without the owner's authorization key returns HTTP 401 with a
"Missing privy-authorization-signature header" error. Surface both as
actionable error messages instead of the generic sanitized failure.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Privy app secret and authorization (owner) key are secrets; the owner key in particular is the root of trust for policy enforcement. Reading them from conf/apiKeys.yml put both on the same host that builds transactions, so a host compromise could read the owner key and rewrite the wallet policy at will, nullifying it entirely. Move both to environment variables (GATEWAY_PRIVY_APP_SECRET, GATEWAY_PRIVY_AUTHORIZATION_KEY) so they are never read from disk config and can be injected at runtime by a secret manager. Only the non-sensitive app ID remains in conf. This is step one; the owner key should ultimately live off the transaction-building host (remote signer / HSM / multi-key owner quorum). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Productionizes the Privy wallet integration from #618 as a fresh, focused branch. The
execute-txendpoints from that PR are not included; this PR keeps and modernizes the Privy wallet layer for both Solana and Ethereum, and threads Privy signing through every connector execution path.Privy core (commit 1)
auth.privy.ioendpoint) with@privy-io/node, lazy-loaded so its ESM-only deps don't affect startup or Jest.apiKeys.privyAuthorizationKey— signing requests carry a P-256 authorization signature; with an owner set on wallets/policies, the app secret alone cannot sign or strip policies.POST /wallet/add-privyreturnspolicyIds/hasOwner/warnings.Connector integration + policy lifecycle (commit 2)
Solana.getWalletType/prepareWallet/signTransactionByTypeandEthereum.getWalletType/getSigner. Connectors no longer branch on a hardware boolean — signing routes by wallet type (local/hardware/privy).Contractflow viaPrivyEvmSigner.POST /wallet/privy-policy: creates an allowlist policy (EVMtoaddresses / Solana program IDs) owned by the configured authorization key; optionally attaches it to a wallet.scripts/test-privy-live.jsfor live verification against real credentials.Known platform constraint (verified against Privy docs + OpenAPI spec, June 2026)
Solana policy conditions support program-ID allowlists and parsed System/Token-program instruction fields only — pool-account pinning is not expressible on Solana (a wallet allowlisted to the Raydium CLMM program can use any Raydium CLMM pool). On EVM, per-pool restriction IS possible via
ethereum_transaction.to+ethereum_calldataABI conditions.Test plan
pnpm test); 25 new Privy tests (routes, policy creation, round-trip verification accept/tamper/wrong-key)pnpm build,pnpm typecheck,pnpm lintcleanscripts/test-privy-live.js)🤖 Generated with Claude Code