-
Notifications
You must be signed in to change notification settings - Fork 699
Refactor wallet provider enum: rename privy→safe, add new privy-only mode #938
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Co-authored-by: hyacinthus <488292+hyacinthus@users.noreply.github.com>
…iders Co-authored-by: hyacinthus <488292+hyacinthus@users.noreply.github.com>
There was a problem hiding this 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 refactors the wallet provider enumeration to clarify the distinction between Privy EOA-only wallets and Privy+Safe smart account wallets. The existing "privy" provider (which created a Privy EOA with a Safe smart account) is renamed to "safe", and a new "privy" provider is introduced that creates only a Privy EOA without deploying a Safe contract. Additionally, x402 skills now validate and reject "safe" mode since Safe's contract-based transactions are incompatible with x402's direct signing requirements.
Changes:
- Wallet provider enum updated: "privy" renamed to "safe", new "privy" mode added for EOA-only wallets
- Agent and user wallet creation logic modified to support both "safe" (Privy+Safe) and "privy" (EOA-only) modes
- x402 skills now validate and reject "safe" wallet provider due to incompatibility with direct signing
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| intentkit/models/agent.py | Updated wallet_provider enum to include "safe" and "privy", updated comments to reflect spending limits are for "safe" mode |
| frontend/src/types/agent.ts | Updated TypeScript type definition to include "safe" in wallet_provider union type |
| intentkit/core/agent.py | Renamed "privy" mode to "safe" for Safe wallet creation, added new "privy" mode for EOA-only wallet creation, updated spending limit sync logic |
| intentkit/core/user.py | Added mode parameter to support both "safe" and "privy" wallet creation for user server wallets |
| intentkit/clients/init.py | Updated wallet provider and signer adapters to handle both "safe" and "privy" modes |
| intentkit/skills/x402/base.py | Added validation to reject "safe" wallet provider since Safe transactions are incompatible with x402 direct signing |
| intentkit/skills/onchain.py | Updated is_onchain_capable to include both "safe" and "privy" as valid on-chain providers |
| tests/skills/test_unified_wallet.py | Updated test from "privy" to "safe" provider |
| tests/core/test_privy_spending_limit_sync.py | Updated tests from "privy" to "safe" provider |
| tests/core/test_privy_owner_validation.py | Updated test from "privy" to "safe" provider |
Comments suppressed due to low confidence (2)
intentkit/models/agent.py:832
- This import of module re is redundant, as it was previously imported on line 6.
import re
intentkit/models/agent.py:1987
- This import of module hashlib is redundant, as it was previously imported on line 3.
import hashlib
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if current_wallet_provider in ("safe", "privy") and old_limit != new_limit: | ||
| agent_data = await AgentData.get(agent.id) | ||
| if agent_data.privy_wallet_data: | ||
| from intentkit.clients.privy import create_privy_safe_wallet | ||
|
|
||
| try: | ||
| privy_wallet_data = json.loads(agent_data.privy_wallet_data) | ||
| except json.JSONDecodeError: | ||
| privy_wallet_data = {} | ||
|
|
||
| existing_privy_wallet_id = privy_wallet_data.get("privy_wallet_id") | ||
| existing_privy_wallet_address = privy_wallet_data.get( | ||
| "privy_wallet_address" | ||
| ) | ||
|
|
||
| if existing_privy_wallet_id and existing_privy_wallet_address: | ||
| rpc_url: str | None = None | ||
| network_id = ( | ||
| agent.network_id | ||
| or privy_wallet_data.get("network_id") | ||
| or "base-mainnet" | ||
| ) | ||
| if config.chain_provider: | ||
| try: | ||
| chain_config = config.chain_provider.get_chain_config( | ||
| network_id | ||
| ) | ||
| rpc_url = chain_config.rpc_url | ||
| except Exception as e: | ||
| logger.warning( | ||
| f"Failed to get RPC URL from chain provider: {e}" | ||
| ) | ||
|
|
||
| wallet_data = await create_privy_safe_wallet( | ||
| agent_id=agent.id, | ||
| network_id=network_id, | ||
| rpc_url=rpc_url, | ||
| weekly_spending_limit_usdc=agent.weekly_spending_limit | ||
| if agent.weekly_spending_limit is not None | ||
| else 0.0, | ||
| existing_privy_wallet_id=existing_privy_wallet_id, | ||
| existing_privy_wallet_address=existing_privy_wallet_address, | ||
| ) | ||
| agent_data = await AgentData.patch( | ||
| agent.id, | ||
| { | ||
| "evm_wallet_address": wallet_data["smart_wallet_address"], | ||
| "privy_wallet_data": json.dumps(wallet_data), | ||
| }, | ||
| # Only safe mode supports spending limits | ||
| if current_wallet_provider == "safe": | ||
| from intentkit.clients.privy import create_privy_safe_wallet | ||
|
|
||
| try: | ||
| privy_wallet_data = json.loads(agent_data.privy_wallet_data) | ||
| except json.JSONDecodeError: | ||
| privy_wallet_data = {} | ||
|
|
||
| existing_privy_wallet_id = privy_wallet_data.get("privy_wallet_id") | ||
| existing_privy_wallet_address = privy_wallet_data.get( | ||
| "privy_wallet_address" | ||
| ) | ||
| return agent_data | ||
|
|
||
| if existing_privy_wallet_id and existing_privy_wallet_address: | ||
| rpc_url: str | None = None | ||
| network_id = ( | ||
| agent.network_id | ||
| or privy_wallet_data.get("network_id") | ||
| or "base-mainnet" | ||
| ) | ||
| if config.chain_provider: | ||
| try: | ||
| chain_config = config.chain_provider.get_chain_config( | ||
| network_id | ||
| ) | ||
| rpc_url = chain_config.rpc_url | ||
| except Exception as e: | ||
| logger.warning( | ||
| f"Failed to get RPC URL from chain provider: {e}" | ||
| ) | ||
|
|
||
| wallet_data = await create_privy_safe_wallet( | ||
| agent_id=agent.id, | ||
| network_id=network_id, | ||
| rpc_url=rpc_url, | ||
| weekly_spending_limit_usdc=agent.weekly_spending_limit | ||
| if agent.weekly_spending_limit is not None | ||
| else 0.0, | ||
| existing_privy_wallet_id=existing_privy_wallet_id, | ||
| existing_privy_wallet_address=existing_privy_wallet_address, | ||
| ) | ||
| agent_data = await AgentData.patch( | ||
| agent.id, | ||
| { | ||
| "evm_wallet_address": wallet_data["smart_wallet_address"], | ||
| "privy_wallet_data": json.dumps(wallet_data), | ||
| }, | ||
| ) | ||
| return agent_data |
Copilot
AI
Jan 19, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The spending limit sync logic at line 107 checks if the wallet provider is "safe" or "privy" and if the spending limit changed. However, it only processes spending limit updates for "safe" mode (line 111). This means that when a user with a "privy" wallet (new privy-only mode) tries to change their spending limit, the code will enter the if block at line 107, but then skip the actual update logic since current_wallet_provider != "safe".
This behavior might be intentional (since privy-only mode doesn't support spending limits according to the PR description). However, it would be better to either:
- Not enter this code path at all for privy mode (change line 107 to only check for "safe")
- Add an explicit check/warning that spending limits are not supported for privy-only mode
| raise ValueError( | ||
| "x402 operations are not supported with 'safe' wallet provider. " | ||
| "Safe wallets use smart contract transactions which are incompatible " | ||
| "with x402 direct signing requirements. Please use 'privy' or 'cdp' wallet provider instead." |
Copilot
AI
Jan 19, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The error message suggests using 'privy' or 'cdp' wallet provider as alternatives to 'safe'. However, in the context of x402 operations, only the new "privy" mode (privy-only, no Safe) is actually compatible with x402's direct signing requirements. The suggestion to use "cdp" is appropriate, but the message could be clearer that "privy" here refers to the privy-only mode, not the old privy+Safe mode (which is now called "safe").
| "with x402 direct signing requirements. Please use 'privy' or 'cdp' wallet provider instead." | |
| "with x402 direct signing requirements. Please use the privy-only " | |
| "wallet provider ('privy', without Safe) or 'cdp' instead; the old " | |
| "Privy+Safe configuration is now called 'safe' and remains unsupported." |
The existing "privy" wallet provider (Privy EOA + Safe smart account) is renamed to "safe". A new "privy" provider is added that creates only a Privy EOA without Safe deployment. x402 skills now validate and reject "safe" mode since Safe's contract-based transactions are incompatible with x402's direct signing requirements.
Changes
Wallet Provider Enum
"privy"→"safe"(Privy EOA + Safe smart account, supports spending limits)"privy"(Privy EOA only, no Safe, direct signing)Agent Wallet Creation (
core/agent.py)evm_wallet_addressevm_wallet_addressUser Wallet Creation (
core/user.py)modeparameter tocreate_user_server_wallet()supporting both "safe" and "privy"x402 Skills (
skills/x402/base.py)Client Adapters (
clients/__init__.py)get_wallet_provider()andget_wallet_signer()to handle both "safe" and "privy"Tests
wallet_provider="privy"towallet_provider="safe"Original prompt
💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.