Skip to content

Commit efb0b55

Browse files
hyacinthusCopilot
andcommitted
feat: add multi-token Safe spending limit support
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent d20dc42 commit efb0b55

7 files changed

Lines changed: 553 additions & 89 deletions

File tree

intentkit/core/agent/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,14 @@
99
from .notifications import send_agent_notification
1010
from .public_info import override_public_info, update_public_info
1111
from .queries import get_agent, get_agent_by_id_or_slug, iterate_agent_id_batches
12-
from .wallet import process_agent_wallet
12+
from .wallet import process_agent_wallet, set_agent_safe_token_spending_limit
1313

1414
__all__ = [
1515
"get_agent",
1616
"get_agent_by_id_or_slug",
1717
"iterate_agent_id_batches",
1818
"process_agent_wallet",
19+
"set_agent_safe_token_spending_limit",
1920
"send_agent_notification",
2021
"override_agent",
2122
"patch_agent",

intentkit/core/agent/wallet.py

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import json
22
import logging
33
from decimal import Decimal
4+
from typing import Any
45

56
from intentkit.config.config import config
67
from intentkit.models.agent import Agent
@@ -279,3 +280,98 @@ async def process_agent_wallet(
279280
)
280281

281282
return agent_data
283+
284+
285+
def _resolve_safe_rpc_url(network_id: str, privy_wallet_data: dict[str, Any]) -> str:
286+
rpc_url = privy_wallet_data.get("rpc_url")
287+
if not rpc_url and config.chain_provider:
288+
try:
289+
chain_config = config.chain_provider.get_chain_config(network_id)
290+
rpc_url = chain_config.rpc_url
291+
except Exception as e:
292+
logger.warning(f"Failed to get RPC URL from chain provider: {e}")
293+
294+
if not rpc_url:
295+
from intentkit.wallets.privy import CHAIN_CONFIGS
296+
297+
chain_config = CHAIN_CONFIGS.get(network_id)
298+
if chain_config and chain_config.rpc_url:
299+
rpc_url = chain_config.rpc_url
300+
301+
if not rpc_url:
302+
raise IntentKitAPIError(
303+
500,
304+
"RpcUrlNotConfigured",
305+
f"RPC URL not configured for network {network_id}",
306+
)
307+
308+
return rpc_url
309+
310+
311+
async def set_agent_safe_token_spending_limit(
312+
agent_id: str,
313+
token_address: str,
314+
spending_limit: float,
315+
) -> dict[str, Any]:
316+
"""Set token spending limit for a Safe agent using agent-level inputs only."""
317+
from intentkit.core.agent.queries import get_agent
318+
from intentkit.wallets.privy import PrivyClient, set_safe_token_spending_limit
319+
320+
agent = await get_agent(agent_id)
321+
if not agent:
322+
raise IntentKitAPIError(
323+
status_code=404,
324+
key="AgentNotFound",
325+
message=f"Agent with ID '{agent_id}' not found",
326+
)
327+
if agent.wallet_provider != "safe":
328+
raise IntentKitAPIError(
329+
400,
330+
"SafeWalletRequired",
331+
"Token spending limits can only be set for agents using wallet_provider='safe'.",
332+
)
333+
334+
agent_data = await AgentData.get(agent_id)
335+
if not agent_data.privy_wallet_data:
336+
raise IntentKitAPIError(
337+
400,
338+
"PrivyWalletDataMissing",
339+
"Privy wallet data is missing for this Safe wallet agent.",
340+
)
341+
342+
try:
343+
privy_wallet_data = json.loads(agent_data.privy_wallet_data)
344+
except json.JSONDecodeError as e:
345+
raise IntentKitAPIError(
346+
500,
347+
"PrivyWalletDataInvalid",
348+
"Privy wallet data is corrupted and cannot be parsed.",
349+
) from e
350+
351+
try:
352+
privy_wallet_id = privy_wallet_data["privy_wallet_id"]
353+
privy_wallet_address = privy_wallet_data["privy_wallet_address"]
354+
safe_address = privy_wallet_data["smart_wallet_address"]
355+
except KeyError as e:
356+
raise IntentKitAPIError(
357+
500,
358+
"PrivyWalletDataIncomplete",
359+
"Privy wallet data is missing required fields.",
360+
) from e
361+
362+
network_id = (
363+
privy_wallet_data.get("network_id") or agent.network_id or "base-mainnet"
364+
)
365+
rpc_url = _resolve_safe_rpc_url(network_id, privy_wallet_data)
366+
367+
privy_client = PrivyClient()
368+
return await set_safe_token_spending_limit(
369+
privy_client=privy_client,
370+
privy_wallet_id=privy_wallet_id,
371+
privy_wallet_address=privy_wallet_address,
372+
safe_address=safe_address,
373+
token_address=token_address,
374+
spending_limit=spending_limit,
375+
network_id=network_id,
376+
rpc_url=rpc_url,
377+
)

intentkit/models/agent_schema.json

Lines changed: 8 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,7 @@
5555
"order": 10
5656
}
5757
],
58-
"required": [
59-
"name"
60-
],
58+
"required": ["name"],
6159
"properties": {
6260
"name": {
6361
"title": "Agent Name",
@@ -153,10 +151,7 @@
153151
"type": "string",
154152
"description": "Strategy for managing short-term memory when context limit is reached. 'trim' removes oldest messages, 'summarize' creates summaries.",
155153
"default": "trim",
156-
"enum": [
157-
"trim",
158-
"summarize"
159-
],
154+
"enum": ["trim", "summarize"],
160155
"x-group": "llm",
161156
"x-advanced": true
162157
},
@@ -322,10 +317,7 @@
322317
"x-group": "autonomous"
323318
}
324319
},
325-
"required": [
326-
"name",
327-
"prompt"
328-
]
320+
"required": ["name", "prompt"]
329321
},
330322
"description": "Set automated prompts and schedules for your agent.",
331323
"x-group": "autonomous",
@@ -335,17 +327,9 @@
335327
"title": "Wallet Provider",
336328
"type": "string",
337329
"description": "Provider of the agent's wallet, choose cdp if you want the agent to have it's own wallet.",
338-
"enum": [
339-
"cdp",
340-
"readonly",
341-
"none"
342-
],
343-
"x-enum-title": [
344-
"Coinbase Server Wallet V2",
345-
"Readonly Wallet",
346-
"None"
347-
],
348-
"default": "cdp",
330+
"enum": ["cdp", "readonly", "none"],
331+
"x-enum-title": ["Coinbase Server Wallet V2", "Readonly Wallet", "None"],
332+
"default": "none",
349333
"x-group": "onchain"
350334
},
351335
"network_id": {
@@ -382,9 +366,7 @@
382366
"x-group": "onchain"
383367
}
384368
},
385-
"required": [
386-
"readonly_wallet_address"
387-
]
369+
"required": ["readonly_wallet_address"]
388370
},
389371
"additionalProperties": true
390-
}
372+
}

intentkit/wallets/privy.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
deploy_safe_with_allowance,
1818
execute_gasless_transaction,
1919
get_wallet_provider,
20+
set_safe_token_spending_limit,
2021
transfer_erc20_gasless,
2122
)
2223
from intentkit.wallets.privy_signer import PrivyWalletSigner, get_wallet_signer
@@ -75,6 +76,7 @@
7576
"_send_safe_transaction_with_master_wallet",
7677
"_send_transaction_with_master_wallet",
7778
"_set_spending_limit",
79+
"set_safe_token_spending_limit",
7880
"_wait_for_safe_deployed",
7981
"create_privy_safe_wallet",
8082
"deploy_safe_with_allowance",

0 commit comments

Comments
 (0)