intentkit 0.7.5.dev15__py3-none-any.whl → 0.7.5.dev17__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of intentkit might be problematic. Click here for more details.
- intentkit/__init__.py +1 -1
- intentkit/models/agent.py +82 -13
- intentkit/models/skills.csv +23 -28
- intentkit/skills/base.py +45 -0
- intentkit/skills/basename/__init__.py +49 -0
- intentkit/skills/basename/base.py +11 -0
- intentkit/skills/basename/basename.svg +11 -0
- intentkit/skills/basename/schema.json +57 -0
- intentkit/skills/cdp/__init__.py +18 -92
- intentkit/skills/cdp/schema.json +13 -301
- intentkit/skills/erc20/__init__.py +50 -0
- intentkit/skills/erc20/base.py +11 -0
- intentkit/skills/erc20/erc20.svg +5 -0
- intentkit/skills/erc20/schema.json +73 -0
- intentkit/skills/erc721/__init__.py +51 -0
- intentkit/skills/erc721/base.py +11 -0
- intentkit/skills/erc721/erc721.svg +5 -0
- intentkit/skills/erc721/schema.json +89 -0
- intentkit/skills/lifi/__init__.py +4 -4
- intentkit/skills/lifi/token_execute.py +24 -12
- intentkit/skills/lifi/token_quote.py +2 -2
- intentkit/skills/lifi/utils.py +96 -43
- intentkit/skills/morpho/__init__.py +50 -0
- intentkit/skills/morpho/base.py +11 -0
- intentkit/skills/morpho/morpho.svg +12 -0
- intentkit/skills/morpho/schema.json +73 -0
- intentkit/skills/pyth/__init__.py +50 -0
- intentkit/skills/pyth/base.py +11 -0
- intentkit/skills/pyth/pyth.svg +6 -0
- intentkit/skills/pyth/schema.json +73 -0
- intentkit/skills/skills.toml +32 -0
- intentkit/skills/superfluid/__init__.py +51 -0
- intentkit/skills/superfluid/base.py +11 -0
- intentkit/skills/superfluid/schema.json +89 -0
- intentkit/skills/superfluid/superfluid.svg +6 -0
- intentkit/skills/weth/__init__.py +49 -0
- intentkit/skills/weth/base.py +11 -0
- intentkit/skills/weth/schema.json +57 -0
- intentkit/skills/weth/weth.svg +6 -0
- intentkit/skills/wow/__init__.py +51 -0
- intentkit/skills/wow/base.py +11 -0
- intentkit/skills/wow/schema.json +89 -0
- intentkit/skills/wow/wow.svg +7 -0
- {intentkit-0.7.5.dev15.dist-info → intentkit-0.7.5.dev17.dist-info}/METADATA +1 -1
- {intentkit-0.7.5.dev15.dist-info → intentkit-0.7.5.dev17.dist-info}/RECORD +47 -17
- intentkit/skills/cdp/get_balance.py +0 -110
- intentkit/skills/cdp/swap.py +0 -121
- {intentkit-0.7.5.dev15.dist-info → intentkit-0.7.5.dev17.dist-info}/WHEEL +0 -0
- {intentkit-0.7.5.dev15.dist-info → intentkit-0.7.5.dev17.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import logging
|
|
2
|
-
from typing import List, Optional, TypedDict
|
|
2
|
+
from typing import Any, List, Optional, TypedDict
|
|
3
3
|
|
|
4
4
|
from intentkit.abstracts.skill import SkillStoreABC
|
|
5
5
|
from intentkit.skills.base import SkillConfig, SkillState
|
|
@@ -32,10 +32,10 @@ async def get_skills(
|
|
|
32
32
|
config: "Config",
|
|
33
33
|
is_private: bool,
|
|
34
34
|
store: SkillStoreABC,
|
|
35
|
-
**_,
|
|
35
|
+
**_: Any,
|
|
36
36
|
) -> list[LiFiBaseTool]:
|
|
37
37
|
"""Get all LiFi skills."""
|
|
38
|
-
available_skills = []
|
|
38
|
+
available_skills: list[str] = []
|
|
39
39
|
|
|
40
40
|
# Log configuration
|
|
41
41
|
logger.info(f"[LiFi_Skills] Initializing with config: {config}")
|
|
@@ -57,7 +57,7 @@ async def get_skills(
|
|
|
57
57
|
logger.info(f"[LiFi_Skills] Available skills: {available_skills}")
|
|
58
58
|
|
|
59
59
|
# Get each skill using the cached getter
|
|
60
|
-
skills = []
|
|
60
|
+
skills: list[LiFiBaseTool] = []
|
|
61
61
|
for name in available_skills:
|
|
62
62
|
try:
|
|
63
63
|
skill = get_lifi_skill(name, store, config)
|
|
@@ -2,11 +2,12 @@ import asyncio
|
|
|
2
2
|
from typing import Any, Dict, List, Optional, Type
|
|
3
3
|
|
|
4
4
|
import httpx
|
|
5
|
+
from coinbase_agentkit import CdpEvmWalletProvider
|
|
5
6
|
from pydantic import BaseModel, Field
|
|
6
7
|
from web3 import Web3
|
|
7
8
|
|
|
8
9
|
from intentkit.abstracts.skill import SkillStoreABC
|
|
9
|
-
from intentkit.clients import get_cdp_client
|
|
10
|
+
from intentkit.clients import CdpClient, get_cdp_client
|
|
10
11
|
from intentkit.skills.lifi.base import LiFiBaseTool
|
|
11
12
|
from intentkit.skills.lifi.token_quote import TokenQuote
|
|
12
13
|
from intentkit.skills.lifi.utils import (
|
|
@@ -69,7 +70,7 @@ class TokenExecute(LiFiBaseTool):
|
|
|
69
70
|
default_slippage: float = 0.03
|
|
70
71
|
allowed_chains: Optional[List[str]] = None
|
|
71
72
|
max_execution_time: int = 300
|
|
72
|
-
quote_tool: TokenQuote = Field(default=None, exclude=True)
|
|
73
|
+
quote_tool: Optional[TokenQuote] = Field(default=None, exclude=True)
|
|
73
74
|
|
|
74
75
|
def __init__(
|
|
75
76
|
self,
|
|
@@ -77,7 +78,7 @@ class TokenExecute(LiFiBaseTool):
|
|
|
77
78
|
default_slippage: float = 0.03,
|
|
78
79
|
allowed_chains: Optional[List[str]] = None,
|
|
79
80
|
max_execution_time: int = 300,
|
|
80
|
-
):
|
|
81
|
+
) -> None:
|
|
81
82
|
"""Initialize the TokenExecute skill with configuration options."""
|
|
82
83
|
super().__init__(skill_store=skill_store)
|
|
83
84
|
self.default_slippage = default_slippage
|
|
@@ -93,6 +94,8 @@ class TokenExecute(LiFiBaseTool):
|
|
|
93
94
|
|
|
94
95
|
def _format_quote_result(self, data: Dict[str, Any]) -> str:
|
|
95
96
|
"""Format the quote result in a readable format."""
|
|
97
|
+
if self.quote_tool is None:
|
|
98
|
+
raise RuntimeError("Quote tool is not initialized")
|
|
96
99
|
# Use the same formatting as token_quote
|
|
97
100
|
return self.quote_tool._format_quote_result(data)
|
|
98
101
|
|
|
@@ -103,7 +106,7 @@ class TokenExecute(LiFiBaseTool):
|
|
|
103
106
|
from_token: str,
|
|
104
107
|
to_token: str,
|
|
105
108
|
from_amount: str,
|
|
106
|
-
slippage: float = None,
|
|
109
|
+
slippage: Optional[float] = None,
|
|
107
110
|
**kwargs,
|
|
108
111
|
) -> str:
|
|
109
112
|
"""Execute a token transfer."""
|
|
@@ -168,7 +171,9 @@ class TokenExecute(LiFiBaseTool):
|
|
|
168
171
|
|
|
169
172
|
# Step 3: Execute transaction
|
|
170
173
|
tx_hash = await self._execute_transfer_transaction(
|
|
171
|
-
cdp_wallet_provider,
|
|
174
|
+
cdp_wallet_provider,
|
|
175
|
+
quote_data,
|
|
176
|
+
from_address,
|
|
172
177
|
)
|
|
173
178
|
|
|
174
179
|
# Step 4: Monitor status and return result
|
|
@@ -180,10 +185,12 @@ class TokenExecute(LiFiBaseTool):
|
|
|
180
185
|
self.logger.error("LiFi_Error: %s", str(e))
|
|
181
186
|
return f"An unexpected error occurred: {str(e)}"
|
|
182
187
|
|
|
183
|
-
async def _get_cdp_wallet_provider(
|
|
188
|
+
async def _get_cdp_wallet_provider(
|
|
189
|
+
self, agent_id: str
|
|
190
|
+
) -> CdpEvmWalletProvider | str:
|
|
184
191
|
"""Get CDP wallet provider with error handling."""
|
|
185
192
|
try:
|
|
186
|
-
cdp_client = await get_cdp_client(agent_id, self.skill_store)
|
|
193
|
+
cdp_client: CdpClient = await get_cdp_client(agent_id, self.skill_store)
|
|
187
194
|
if not cdp_client:
|
|
188
195
|
return "CDP client not available. Please ensure your agent has CDP wallet configuration."
|
|
189
196
|
|
|
@@ -207,7 +214,7 @@ class TokenExecute(LiFiBaseTool):
|
|
|
207
214
|
from_amount: str,
|
|
208
215
|
slippage: float,
|
|
209
216
|
from_address: str,
|
|
210
|
-
) -> Dict[str, Any]:
|
|
217
|
+
) -> Dict[str, Any] | str:
|
|
211
218
|
"""Get quote from LiFi API."""
|
|
212
219
|
api_params = build_quote_params(
|
|
213
220
|
from_chain,
|
|
@@ -249,7 +256,7 @@ class TokenExecute(LiFiBaseTool):
|
|
|
249
256
|
return data
|
|
250
257
|
|
|
251
258
|
async def _handle_token_approval(
|
|
252
|
-
self, wallet_provider, quote_data: Dict[str, Any]
|
|
259
|
+
self, wallet_provider: CdpEvmWalletProvider, quote_data: Dict[str, Any]
|
|
253
260
|
) -> Optional[str]:
|
|
254
261
|
"""Handle ERC20 token approval if needed."""
|
|
255
262
|
estimate = quote_data.get("estimate", {})
|
|
@@ -273,13 +280,18 @@ class TokenExecute(LiFiBaseTool):
|
|
|
273
280
|
raise Exception(f"Failed to approve token: {str(e)}")
|
|
274
281
|
|
|
275
282
|
async def _execute_transfer_transaction(
|
|
276
|
-
self,
|
|
283
|
+
self,
|
|
284
|
+
wallet_provider: CdpEvmWalletProvider,
|
|
285
|
+
quote_data: Dict[str, Any],
|
|
286
|
+
from_address: str,
|
|
277
287
|
) -> str:
|
|
278
288
|
"""Execute the main transfer transaction."""
|
|
279
289
|
transaction_request = quote_data.get("transactionRequest")
|
|
280
290
|
|
|
281
291
|
try:
|
|
282
|
-
tx_params = prepare_transaction_params(
|
|
292
|
+
tx_params = prepare_transaction_params(
|
|
293
|
+
transaction_request, wallet_address=from_address
|
|
294
|
+
)
|
|
283
295
|
self.logger.info(
|
|
284
296
|
f"Sending transaction to {tx_params['to']} with value {tx_params['value']}"
|
|
285
297
|
)
|
|
@@ -407,7 +419,7 @@ class TokenExecute(LiFiBaseTool):
|
|
|
407
419
|
|
|
408
420
|
async def _check_and_set_allowance(
|
|
409
421
|
self,
|
|
410
|
-
wallet_provider,
|
|
422
|
+
wallet_provider: CdpEvmWalletProvider,
|
|
411
423
|
token_address: str,
|
|
412
424
|
approval_address: str,
|
|
413
425
|
amount: str,
|
|
@@ -65,7 +65,7 @@ class TokenQuote(LiFiBaseTool):
|
|
|
65
65
|
skill_store: SkillStoreABC,
|
|
66
66
|
default_slippage: float = 0.03,
|
|
67
67
|
allowed_chains: Optional[List[str]] = None,
|
|
68
|
-
):
|
|
68
|
+
) -> None:
|
|
69
69
|
"""Initialize the TokenQuote skill with configuration options."""
|
|
70
70
|
super().__init__(skill_store=skill_store)
|
|
71
71
|
self.default_slippage = default_slippage
|
|
@@ -78,7 +78,7 @@ class TokenQuote(LiFiBaseTool):
|
|
|
78
78
|
from_token: str,
|
|
79
79
|
to_token: str,
|
|
80
80
|
from_amount: str,
|
|
81
|
-
slippage: float = None,
|
|
81
|
+
slippage: Optional[float] = None,
|
|
82
82
|
**kwargs,
|
|
83
83
|
) -> str:
|
|
84
84
|
"""Get a quote for token transfer."""
|
intentkit/skills/lifi/utils.py
CHANGED
|
@@ -4,6 +4,7 @@ LiFi Skills Utilities
|
|
|
4
4
|
Common utilities and helper functions for LiFi token transfer skills.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
+
from decimal import ROUND_DOWN, Decimal, InvalidOperation
|
|
7
8
|
from typing import Any, Dict, List, Optional, Tuple
|
|
8
9
|
|
|
9
10
|
import httpx
|
|
@@ -179,7 +180,7 @@ def handle_api_response(
|
|
|
179
180
|
from_chain: str,
|
|
180
181
|
to_token: str,
|
|
181
182
|
to_chain: str,
|
|
182
|
-
) -> Tuple[Optional[Dict], Optional[str]]:
|
|
183
|
+
) -> Tuple[Optional[Dict[str, Any]], Optional[str]]:
|
|
183
184
|
"""
|
|
184
185
|
Handle LiFi API response and return data or error message.
|
|
185
186
|
|
|
@@ -293,17 +294,20 @@ def convert_chain_to_id(chain: str) -> int:
|
|
|
293
294
|
|
|
294
295
|
|
|
295
296
|
def convert_amount_to_wei(amount: str, token_symbol: str = "ETH") -> str:
|
|
296
|
-
"""
|
|
297
|
-
Convert human-readable amount to wei format for LiFi API.
|
|
297
|
+
"""Convert a token amount into the smallest denomination expected by LiFi."""
|
|
298
298
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
299
|
+
if amount is None:
|
|
300
|
+
raise ValueError("Amount is required")
|
|
301
|
+
|
|
302
|
+
normalized_amount = amount.strip()
|
|
303
|
+
if not normalized_amount:
|
|
304
|
+
raise ValueError("Amount cannot be empty")
|
|
305
|
+
|
|
306
|
+
# If the user already provided an integer amount without a decimal point,
|
|
307
|
+
# assume it is already in the token's smallest denomination.
|
|
308
|
+
if normalized_amount.isdigit():
|
|
309
|
+
return normalized_amount
|
|
302
310
|
|
|
303
|
-
Returns:
|
|
304
|
-
Amount in wei format as string
|
|
305
|
-
"""
|
|
306
|
-
# Default decimals for common tokens
|
|
307
311
|
token_decimals = {
|
|
308
312
|
"ETH": 18,
|
|
309
313
|
"USDC": 6,
|
|
@@ -318,13 +322,16 @@ def convert_amount_to_wei(amount: str, token_symbol: str = "ETH") -> str:
|
|
|
318
322
|
decimals = token_decimals.get(token_symbol.upper(), 18)
|
|
319
323
|
|
|
320
324
|
try:
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
325
|
+
decimal_amount = Decimal(normalized_amount)
|
|
326
|
+
scaled_amount = (decimal_amount * (Decimal(10) ** decimals)).quantize(
|
|
327
|
+
Decimal("1"),
|
|
328
|
+
rounding=ROUND_DOWN,
|
|
329
|
+
)
|
|
330
|
+
return str(int(scaled_amount))
|
|
331
|
+
except (InvalidOperation, ValueError, TypeError):
|
|
332
|
+
# If conversion fails, fall back to the original value to avoid
|
|
333
|
+
# accidentally submitting an incorrect amount.
|
|
334
|
+
return normalized_amount
|
|
328
335
|
|
|
329
336
|
|
|
330
337
|
def build_quote_params(
|
|
@@ -350,15 +357,12 @@ def build_quote_params(
|
|
|
350
357
|
Raises:
|
|
351
358
|
ValueError: If chain identifiers are not recognized
|
|
352
359
|
"""
|
|
353
|
-
# Convert amount to wei format for API
|
|
354
|
-
wei_amount = convert_amount_to_wei(from_amount, from_token)
|
|
355
|
-
|
|
356
360
|
return {
|
|
357
361
|
"fromChain": convert_chain_to_id(from_chain),
|
|
358
362
|
"toChain": convert_chain_to_id(to_chain),
|
|
359
363
|
"fromToken": from_token,
|
|
360
364
|
"toToken": to_token,
|
|
361
|
-
"fromAmount":
|
|
365
|
+
"fromAmount": convert_amount_to_wei(from_amount, from_token),
|
|
362
366
|
"fromAddress": from_address or DUMMY_ADDRESS,
|
|
363
367
|
"slippage": slippage,
|
|
364
368
|
}
|
|
@@ -381,36 +385,85 @@ def is_native_token(token_address: str) -> bool:
|
|
|
381
385
|
)
|
|
382
386
|
|
|
383
387
|
|
|
384
|
-
def
|
|
385
|
-
"""
|
|
386
|
-
Prepare transaction parameters for CDP wallet provider.
|
|
388
|
+
def _convert_hex_or_decimal(value: Any) -> Optional[int]:
|
|
389
|
+
"""Convert LiFi transaction numeric values into integers."""
|
|
387
390
|
|
|
388
|
-
|
|
389
|
-
|
|
391
|
+
if value is None:
|
|
392
|
+
return None
|
|
390
393
|
|
|
391
|
-
|
|
392
|
-
|
|
394
|
+
if isinstance(value, int):
|
|
395
|
+
return value
|
|
396
|
+
|
|
397
|
+
if isinstance(value, str):
|
|
398
|
+
stripped = value.strip()
|
|
399
|
+
if not stripped:
|
|
400
|
+
return None
|
|
401
|
+
if stripped.startswith("0x"):
|
|
402
|
+
return int(stripped, 16)
|
|
403
|
+
try:
|
|
404
|
+
return int(Decimal(stripped))
|
|
405
|
+
except (InvalidOperation, ValueError):
|
|
406
|
+
return None
|
|
407
|
+
|
|
408
|
+
return None
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
def prepare_transaction_params(
|
|
412
|
+
transaction_request: Dict[str, Any],
|
|
413
|
+
wallet_address: Optional[str] = None,
|
|
414
|
+
) -> Dict[str, Any]:
|
|
415
|
+
"""Prepare transaction parameters for the CDP wallet provider."""
|
|
393
416
|
|
|
394
|
-
Raises:
|
|
395
|
-
Exception: If required parameters are missing
|
|
396
|
-
"""
|
|
397
417
|
to_address = transaction_request.get("to")
|
|
398
|
-
value = transaction_request.get("value", "
|
|
418
|
+
value = transaction_request.get("value", "0x0")
|
|
399
419
|
data = transaction_request.get("data", "0x")
|
|
400
420
|
|
|
401
421
|
if not to_address:
|
|
402
|
-
raise Exception("
|
|
422
|
+
raise Exception("Transaction request is missing destination address")
|
|
403
423
|
|
|
404
|
-
|
|
405
|
-
if isinstance(value, str):
|
|
406
|
-
value = int(value, 16) if value.startswith("0x") else int(value)
|
|
407
|
-
|
|
408
|
-
return {
|
|
424
|
+
tx_params: Dict[str, Any] = {
|
|
409
425
|
"to": Web3.to_checksum_address(to_address),
|
|
410
|
-
"value": value,
|
|
411
426
|
"data": data,
|
|
412
427
|
}
|
|
413
428
|
|
|
429
|
+
int_value = _convert_hex_or_decimal(value)
|
|
430
|
+
if int_value is not None:
|
|
431
|
+
tx_params["value"] = int_value
|
|
432
|
+
|
|
433
|
+
chain_id = _convert_hex_or_decimal(transaction_request.get("chainId"))
|
|
434
|
+
if chain_id is not None:
|
|
435
|
+
tx_params["chainId"] = chain_id
|
|
436
|
+
|
|
437
|
+
gas_limit = _convert_hex_or_decimal(
|
|
438
|
+
transaction_request.get("gasLimit") or transaction_request.get("gas")
|
|
439
|
+
)
|
|
440
|
+
if gas_limit is not None:
|
|
441
|
+
tx_params["gas"] = gas_limit
|
|
442
|
+
|
|
443
|
+
gas_price = _convert_hex_or_decimal(transaction_request.get("gasPrice"))
|
|
444
|
+
if gas_price is not None:
|
|
445
|
+
tx_params["gasPrice"] = gas_price
|
|
446
|
+
|
|
447
|
+
max_fee_per_gas = _convert_hex_or_decimal(transaction_request.get("maxFeePerGas"))
|
|
448
|
+
if max_fee_per_gas is not None:
|
|
449
|
+
tx_params["maxFeePerGas"] = max_fee_per_gas
|
|
450
|
+
|
|
451
|
+
max_priority_fee_per_gas = _convert_hex_or_decimal(
|
|
452
|
+
transaction_request.get("maxPriorityFeePerGas")
|
|
453
|
+
)
|
|
454
|
+
if max_priority_fee_per_gas is not None:
|
|
455
|
+
tx_params["maxPriorityFeePerGas"] = max_priority_fee_per_gas
|
|
456
|
+
|
|
457
|
+
nonce = _convert_hex_or_decimal(transaction_request.get("nonce"))
|
|
458
|
+
if nonce is not None:
|
|
459
|
+
tx_params["nonce"] = nonce
|
|
460
|
+
|
|
461
|
+
from_address = transaction_request.get("from") or wallet_address
|
|
462
|
+
if from_address:
|
|
463
|
+
tx_params["from"] = Web3.to_checksum_address(from_address)
|
|
464
|
+
|
|
465
|
+
return tx_params
|
|
466
|
+
|
|
414
467
|
|
|
415
468
|
def format_quote_basic_info(data: Dict[str, Any]) -> Dict[str, Any]:
|
|
416
469
|
"""
|
|
@@ -464,7 +517,7 @@ def format_fees_and_gas(data: Dict[str, Any]) -> Tuple[str, str]:
|
|
|
464
517
|
|
|
465
518
|
# Extract gas and fee costs
|
|
466
519
|
gas_costs = estimate.get("gasCosts", [])
|
|
467
|
-
fee_costs = []
|
|
520
|
+
fee_costs: List[Dict[str, Any]] = []
|
|
468
521
|
|
|
469
522
|
# Collect fee information from included steps
|
|
470
523
|
for step in data.get("includedSteps", []):
|
|
@@ -476,7 +529,7 @@ def format_fees_and_gas(data: Dict[str, Any]) -> Tuple[str, str]:
|
|
|
476
529
|
fees_text = ""
|
|
477
530
|
if fee_costs:
|
|
478
531
|
fees_text = "**Fees:**\n"
|
|
479
|
-
total_fee_usd = 0
|
|
532
|
+
total_fee_usd = 0.0
|
|
480
533
|
for fee in fee_costs:
|
|
481
534
|
fee_name = fee.get("name", "Unknown fee")
|
|
482
535
|
fee_amount = fee.get("amount", "0")
|
|
@@ -508,7 +561,7 @@ def format_fees_and_gas(data: Dict[str, Any]) -> Tuple[str, str]:
|
|
|
508
561
|
gas_text = ""
|
|
509
562
|
if gas_costs:
|
|
510
563
|
gas_text = "**Gas Cost:**\n"
|
|
511
|
-
total_gas_usd = 0
|
|
564
|
+
total_gas_usd = 0.0
|
|
512
565
|
for gas in gas_costs:
|
|
513
566
|
gas_amount = gas.get("amount", "0")
|
|
514
567
|
gas_token = gas.get("token", {}).get("symbol", "ETH")
|
|
@@ -628,7 +681,7 @@ def get_explorer_url(chain_id: int, tx_hash: str) -> str:
|
|
|
628
681
|
|
|
629
682
|
|
|
630
683
|
def format_transaction_result(
|
|
631
|
-
tx_hash: str, chain_id: int, token_info:
|
|
684
|
+
tx_hash: str, chain_id: int, token_info: Optional[Dict[str, str]] = None
|
|
632
685
|
) -> str:
|
|
633
686
|
"""
|
|
634
687
|
Format transaction result with explorer link.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""Morpho AgentKit skills."""
|
|
2
|
+
|
|
3
|
+
from typing import TypedDict
|
|
4
|
+
|
|
5
|
+
from coinbase_agentkit import morpho_action_provider
|
|
6
|
+
|
|
7
|
+
from intentkit.abstracts.skill import SkillStoreABC
|
|
8
|
+
from intentkit.skills.base import (
|
|
9
|
+
SkillConfig,
|
|
10
|
+
SkillState,
|
|
11
|
+
action_to_structured_tool,
|
|
12
|
+
get_agentkit_actions,
|
|
13
|
+
)
|
|
14
|
+
from intentkit.skills.morpho.base import MorphoBaseTool
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class SkillStates(TypedDict):
|
|
18
|
+
MorphoActionProvider_deposit: SkillState
|
|
19
|
+
MorphoActionProvider_withdraw: SkillState
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class Config(SkillConfig):
|
|
23
|
+
"""Configuration for Morpho skills."""
|
|
24
|
+
|
|
25
|
+
states: SkillStates
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
async def get_skills(
|
|
29
|
+
config: "Config",
|
|
30
|
+
is_private: bool,
|
|
31
|
+
store: SkillStoreABC,
|
|
32
|
+
agent_id: str,
|
|
33
|
+
**_,
|
|
34
|
+
) -> list[MorphoBaseTool]:
|
|
35
|
+
"""Get all Morpho skills."""
|
|
36
|
+
|
|
37
|
+
available_skills: list[str] = []
|
|
38
|
+
for skill_name, state in config["states"].items():
|
|
39
|
+
if state == "disabled":
|
|
40
|
+
continue
|
|
41
|
+
if state == "public" or (state == "private" and is_private):
|
|
42
|
+
available_skills.append(skill_name)
|
|
43
|
+
|
|
44
|
+
actions = await get_agentkit_actions(agent_id, store, [morpho_action_provider])
|
|
45
|
+
tools: list[MorphoBaseTool] = []
|
|
46
|
+
for skill in available_skills:
|
|
47
|
+
for action in actions:
|
|
48
|
+
if action.name.endswith(skill):
|
|
49
|
+
tools.append(action_to_structured_tool(action))
|
|
50
|
+
return tools
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128">
|
|
2
|
+
<defs>
|
|
3
|
+
<radialGradient id="morpho" cx="50%" cy="50%" r="70%">
|
|
4
|
+
<stop offset="0%" stop-color="#82f3ff" />
|
|
5
|
+
<stop offset="100%" stop-color="#006064" />
|
|
6
|
+
</radialGradient>
|
|
7
|
+
</defs>
|
|
8
|
+
<rect width="128" height="128" fill="#004d40" rx="16" />
|
|
9
|
+
<circle cx="64" cy="48" r="28" fill="url(#morpho)" />
|
|
10
|
+
<rect x="36" y="72" width="56" height="20" rx="10" fill="#26a69a" />
|
|
11
|
+
<text x="50%" y="84%" font-family="Arial,Helvetica,sans-serif" font-size="18" fill="#e0f2f1" font-weight="600" text-anchor="middle">Morpho</text>
|
|
12
|
+
</svg>
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"type": "object",
|
|
4
|
+
"title": "Morpho",
|
|
5
|
+
"description": "Morpho lending actions via Coinbase AgentKit",
|
|
6
|
+
"x-icon": "https://ai.service.crestal.dev/skills/morpho/morpho.svg",
|
|
7
|
+
"x-tags": [
|
|
8
|
+
"Blockchain"
|
|
9
|
+
],
|
|
10
|
+
"properties": {
|
|
11
|
+
"enabled": {
|
|
12
|
+
"type": "boolean",
|
|
13
|
+
"title": "Enabled",
|
|
14
|
+
"description": "Whether this skill is enabled",
|
|
15
|
+
"default": true
|
|
16
|
+
},
|
|
17
|
+
"states": {
|
|
18
|
+
"type": "object",
|
|
19
|
+
"properties": {
|
|
20
|
+
"MorphoActionProvider_deposit": {
|
|
21
|
+
"type": "string",
|
|
22
|
+
"title": "Deposit",
|
|
23
|
+
"enum": [
|
|
24
|
+
"disabled",
|
|
25
|
+
"public",
|
|
26
|
+
"private"
|
|
27
|
+
],
|
|
28
|
+
"x-enum-title": [
|
|
29
|
+
"Disabled",
|
|
30
|
+
"Agent Owner + All Users",
|
|
31
|
+
"Agent Owner Only"
|
|
32
|
+
],
|
|
33
|
+
"description": "State for MorphoActionProvider_deposit",
|
|
34
|
+
"default": "disabled"
|
|
35
|
+
},
|
|
36
|
+
"MorphoActionProvider_withdraw": {
|
|
37
|
+
"type": "string",
|
|
38
|
+
"title": "Withdraw",
|
|
39
|
+
"enum": [
|
|
40
|
+
"disabled",
|
|
41
|
+
"public",
|
|
42
|
+
"private"
|
|
43
|
+
],
|
|
44
|
+
"x-enum-title": [
|
|
45
|
+
"Disabled",
|
|
46
|
+
"Agent Owner + All Users",
|
|
47
|
+
"Agent Owner Only"
|
|
48
|
+
],
|
|
49
|
+
"description": "State for MorphoActionProvider_withdraw",
|
|
50
|
+
"default": "disabled"
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
"description": "States for each Morpho skill (disabled, public, or private)"
|
|
54
|
+
},
|
|
55
|
+
"api_key_provider": {
|
|
56
|
+
"type": "string",
|
|
57
|
+
"title": "API Key Provider",
|
|
58
|
+
"description": "Who provides the API key",
|
|
59
|
+
"enum": [
|
|
60
|
+
"platform"
|
|
61
|
+
],
|
|
62
|
+
"x-enum-title": [
|
|
63
|
+
"Nation Hosted"
|
|
64
|
+
],
|
|
65
|
+
"default": "platform"
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
"required": [
|
|
69
|
+
"states",
|
|
70
|
+
"enabled"
|
|
71
|
+
],
|
|
72
|
+
"additionalProperties": true
|
|
73
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""Pyth AgentKit skills."""
|
|
2
|
+
|
|
3
|
+
from typing import TypedDict
|
|
4
|
+
|
|
5
|
+
from coinbase_agentkit import pyth_action_provider
|
|
6
|
+
|
|
7
|
+
from intentkit.abstracts.skill import SkillStoreABC
|
|
8
|
+
from intentkit.skills.base import (
|
|
9
|
+
SkillConfig,
|
|
10
|
+
SkillState,
|
|
11
|
+
action_to_structured_tool,
|
|
12
|
+
get_agentkit_actions,
|
|
13
|
+
)
|
|
14
|
+
from intentkit.skills.pyth.base import PythBaseTool
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class SkillStates(TypedDict):
|
|
18
|
+
PythActionProvider_fetch_price: SkillState
|
|
19
|
+
PythActionProvider_fetch_price_feed: SkillState
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class Config(SkillConfig):
|
|
23
|
+
"""Configuration for Pyth skills."""
|
|
24
|
+
|
|
25
|
+
states: SkillStates
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
async def get_skills(
|
|
29
|
+
config: "Config",
|
|
30
|
+
is_private: bool,
|
|
31
|
+
store: SkillStoreABC,
|
|
32
|
+
agent_id: str,
|
|
33
|
+
**_,
|
|
34
|
+
) -> list[PythBaseTool]:
|
|
35
|
+
"""Get all Pyth skills."""
|
|
36
|
+
|
|
37
|
+
available_skills: list[str] = []
|
|
38
|
+
for skill_name, state in config["states"].items():
|
|
39
|
+
if state == "disabled":
|
|
40
|
+
continue
|
|
41
|
+
if state == "public" or (state == "private" and is_private):
|
|
42
|
+
available_skills.append(skill_name)
|
|
43
|
+
|
|
44
|
+
actions = await get_agentkit_actions(agent_id, store, [pyth_action_provider])
|
|
45
|
+
tools: list[PythBaseTool] = []
|
|
46
|
+
for skill in available_skills:
|
|
47
|
+
for action in actions:
|
|
48
|
+
if action.name.endswith(skill):
|
|
49
|
+
tools.append(action_to_structured_tool(action))
|
|
50
|
+
return tools
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128">
|
|
2
|
+
<rect width="128" height="128" fill="#111827" rx="16" />
|
|
3
|
+
<path d="M40 44h48l-12 40H28z" fill="#38bdf8" opacity="0.85" />
|
|
4
|
+
<path d="M88 84H40l12-40h48z" fill="#f472b6" opacity="0.75" />
|
|
5
|
+
<text x="50%" y="92" font-family="Arial,Helvetica,sans-serif" font-size="18" fill="#f8fafc" text-anchor="middle">PYTH</text>
|
|
6
|
+
</svg>
|