intentkit 0.5.0__py3-none-any.whl → 0.5.2__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.

Files changed (366) hide show
  1. intentkit/__init__.py +17 -0
  2. intentkit/abstracts/__init__.py +0 -0
  3. intentkit/abstracts/agent.py +60 -0
  4. intentkit/abstracts/api.py +4 -0
  5. intentkit/abstracts/engine.py +38 -0
  6. intentkit/abstracts/exception.py +9 -0
  7. intentkit/abstracts/graph.py +25 -0
  8. intentkit/abstracts/skill.py +129 -0
  9. intentkit/abstracts/twitter.py +54 -0
  10. intentkit/clients/__init__.py +14 -0
  11. intentkit/clients/cdp.py +53 -0
  12. intentkit/clients/twitter.py +445 -0
  13. intentkit/config/__init__.py +0 -0
  14. intentkit/config/config.py +164 -0
  15. intentkit/core/__init__.py +0 -0
  16. intentkit/core/agent.py +191 -0
  17. intentkit/core/api.py +40 -0
  18. intentkit/core/client.py +45 -0
  19. intentkit/core/credit.py +1767 -0
  20. intentkit/core/engine.py +1018 -0
  21. intentkit/core/node.py +223 -0
  22. intentkit/core/prompt.py +58 -0
  23. intentkit/core/skill.py +124 -0
  24. intentkit/models/agent.py +1689 -0
  25. intentkit/models/agent_data.py +810 -0
  26. intentkit/models/agent_schema.json +733 -0
  27. intentkit/models/app_setting.py +156 -0
  28. intentkit/models/base.py +9 -0
  29. intentkit/models/chat.py +581 -0
  30. intentkit/models/conversation.py +286 -0
  31. intentkit/models/credit.py +1406 -0
  32. intentkit/models/db.py +120 -0
  33. intentkit/models/db_mig.py +102 -0
  34. intentkit/models/generator.py +347 -0
  35. intentkit/models/llm.py +746 -0
  36. intentkit/models/redis.py +132 -0
  37. intentkit/models/skill.py +466 -0
  38. intentkit/models/user.py +243 -0
  39. intentkit/skills/__init__.py +12 -0
  40. intentkit/skills/acolyt/__init__.py +83 -0
  41. intentkit/skills/acolyt/acolyt.jpg +0 -0
  42. intentkit/skills/acolyt/ask.py +128 -0
  43. intentkit/skills/acolyt/base.py +28 -0
  44. intentkit/skills/acolyt/schema.json +89 -0
  45. intentkit/skills/aixbt/README.md +71 -0
  46. intentkit/skills/aixbt/__init__.py +73 -0
  47. intentkit/skills/aixbt/aixbt.jpg +0 -0
  48. intentkit/skills/aixbt/base.py +21 -0
  49. intentkit/skills/aixbt/projects.py +153 -0
  50. intentkit/skills/aixbt/schema.json +99 -0
  51. intentkit/skills/allora/__init__.py +83 -0
  52. intentkit/skills/allora/allora.jpeg +0 -0
  53. intentkit/skills/allora/base.py +28 -0
  54. intentkit/skills/allora/price.py +130 -0
  55. intentkit/skills/allora/schema.json +89 -0
  56. intentkit/skills/base.py +174 -0
  57. intentkit/skills/carv/README.md +95 -0
  58. intentkit/skills/carv/__init__.py +121 -0
  59. intentkit/skills/carv/base.py +183 -0
  60. intentkit/skills/carv/carv.webp +0 -0
  61. intentkit/skills/carv/fetch_news.py +92 -0
  62. intentkit/skills/carv/onchain_query.py +164 -0
  63. intentkit/skills/carv/schema.json +137 -0
  64. intentkit/skills/carv/token_info_and_price.py +110 -0
  65. intentkit/skills/cdp/__init__.py +137 -0
  66. intentkit/skills/cdp/base.py +21 -0
  67. intentkit/skills/cdp/cdp.png +0 -0
  68. intentkit/skills/cdp/get_balance.py +81 -0
  69. intentkit/skills/cdp/schema.json +473 -0
  70. intentkit/skills/chainlist/README.md +38 -0
  71. intentkit/skills/chainlist/__init__.py +54 -0
  72. intentkit/skills/chainlist/base.py +21 -0
  73. intentkit/skills/chainlist/chain_lookup.py +208 -0
  74. intentkit/skills/chainlist/chainlist.png +0 -0
  75. intentkit/skills/chainlist/schema.json +47 -0
  76. intentkit/skills/common/__init__.py +82 -0
  77. intentkit/skills/common/base.py +21 -0
  78. intentkit/skills/common/common.jpg +0 -0
  79. intentkit/skills/common/current_time.py +84 -0
  80. intentkit/skills/common/schema.json +57 -0
  81. intentkit/skills/cookiefun/README.md +121 -0
  82. intentkit/skills/cookiefun/__init__.py +78 -0
  83. intentkit/skills/cookiefun/base.py +41 -0
  84. intentkit/skills/cookiefun/constants.py +18 -0
  85. intentkit/skills/cookiefun/cookiefun.png +0 -0
  86. intentkit/skills/cookiefun/get_account_details.py +171 -0
  87. intentkit/skills/cookiefun/get_account_feed.py +282 -0
  88. intentkit/skills/cookiefun/get_account_smart_followers.py +181 -0
  89. intentkit/skills/cookiefun/get_sectors.py +128 -0
  90. intentkit/skills/cookiefun/schema.json +155 -0
  91. intentkit/skills/cookiefun/search_accounts.py +225 -0
  92. intentkit/skills/cryptocompare/__init__.py +130 -0
  93. intentkit/skills/cryptocompare/api.py +159 -0
  94. intentkit/skills/cryptocompare/base.py +303 -0
  95. intentkit/skills/cryptocompare/cryptocompare.png +0 -0
  96. intentkit/skills/cryptocompare/fetch_news.py +96 -0
  97. intentkit/skills/cryptocompare/fetch_price.py +99 -0
  98. intentkit/skills/cryptocompare/fetch_top_exchanges.py +113 -0
  99. intentkit/skills/cryptocompare/fetch_top_market_cap.py +109 -0
  100. intentkit/skills/cryptocompare/fetch_top_volume.py +108 -0
  101. intentkit/skills/cryptocompare/fetch_trading_signals.py +107 -0
  102. intentkit/skills/cryptocompare/schema.json +168 -0
  103. intentkit/skills/cryptopanic/__init__.py +108 -0
  104. intentkit/skills/cryptopanic/base.py +51 -0
  105. intentkit/skills/cryptopanic/cryptopanic.png +0 -0
  106. intentkit/skills/cryptopanic/fetch_crypto_news.py +153 -0
  107. intentkit/skills/cryptopanic/fetch_crypto_sentiment.py +136 -0
  108. intentkit/skills/cryptopanic/schema.json +103 -0
  109. intentkit/skills/dapplooker/README.md +92 -0
  110. intentkit/skills/dapplooker/__init__.py +83 -0
  111. intentkit/skills/dapplooker/base.py +26 -0
  112. intentkit/skills/dapplooker/dapplooker.jpg +0 -0
  113. intentkit/skills/dapplooker/dapplooker_token_data.py +476 -0
  114. intentkit/skills/dapplooker/schema.json +91 -0
  115. intentkit/skills/defillama/__init__.py +323 -0
  116. intentkit/skills/defillama/api.py +315 -0
  117. intentkit/skills/defillama/base.py +135 -0
  118. intentkit/skills/defillama/coins/__init__.py +0 -0
  119. intentkit/skills/defillama/coins/fetch_batch_historical_prices.py +116 -0
  120. intentkit/skills/defillama/coins/fetch_block.py +98 -0
  121. intentkit/skills/defillama/coins/fetch_current_prices.py +105 -0
  122. intentkit/skills/defillama/coins/fetch_first_price.py +100 -0
  123. intentkit/skills/defillama/coins/fetch_historical_prices.py +110 -0
  124. intentkit/skills/defillama/coins/fetch_price_chart.py +109 -0
  125. intentkit/skills/defillama/coins/fetch_price_percentage.py +93 -0
  126. intentkit/skills/defillama/config/__init__.py +0 -0
  127. intentkit/skills/defillama/config/chains.py +433 -0
  128. intentkit/skills/defillama/defillama.jpeg +0 -0
  129. intentkit/skills/defillama/fees/__init__.py +0 -0
  130. intentkit/skills/defillama/fees/fetch_fees_overview.py +130 -0
  131. intentkit/skills/defillama/schema.json +383 -0
  132. intentkit/skills/defillama/stablecoins/__init__.py +0 -0
  133. intentkit/skills/defillama/stablecoins/fetch_stablecoin_chains.py +100 -0
  134. intentkit/skills/defillama/stablecoins/fetch_stablecoin_charts.py +129 -0
  135. intentkit/skills/defillama/stablecoins/fetch_stablecoin_prices.py +83 -0
  136. intentkit/skills/defillama/stablecoins/fetch_stablecoins.py +126 -0
  137. intentkit/skills/defillama/tests/__init__.py +0 -0
  138. intentkit/skills/defillama/tests/api_integration.test.py +192 -0
  139. intentkit/skills/defillama/tests/api_unit.test.py +583 -0
  140. intentkit/skills/defillama/tvl/__init__.py +0 -0
  141. intentkit/skills/defillama/tvl/fetch_chain_historical_tvl.py +106 -0
  142. intentkit/skills/defillama/tvl/fetch_chains.py +107 -0
  143. intentkit/skills/defillama/tvl/fetch_historical_tvl.py +91 -0
  144. intentkit/skills/defillama/tvl/fetch_protocol.py +207 -0
  145. intentkit/skills/defillama/tvl/fetch_protocol_current_tvl.py +93 -0
  146. intentkit/skills/defillama/tvl/fetch_protocols.py +196 -0
  147. intentkit/skills/defillama/volumes/__init__.py +0 -0
  148. intentkit/skills/defillama/volumes/fetch_dex_overview.py +157 -0
  149. intentkit/skills/defillama/volumes/fetch_dex_summary.py +123 -0
  150. intentkit/skills/defillama/volumes/fetch_options_overview.py +131 -0
  151. intentkit/skills/defillama/yields/__init__.py +0 -0
  152. intentkit/skills/defillama/yields/fetch_pool_chart.py +100 -0
  153. intentkit/skills/defillama/yields/fetch_pools.py +126 -0
  154. intentkit/skills/dexscreener/__init__.py +93 -0
  155. intentkit/skills/dexscreener/base.py +133 -0
  156. intentkit/skills/dexscreener/dexscreener.png +0 -0
  157. intentkit/skills/dexscreener/model/__init__.py +0 -0
  158. intentkit/skills/dexscreener/model/search_token_response.py +82 -0
  159. intentkit/skills/dexscreener/schema.json +48 -0
  160. intentkit/skills/dexscreener/search_token.py +321 -0
  161. intentkit/skills/dune_analytics/__init__.py +103 -0
  162. intentkit/skills/dune_analytics/base.py +46 -0
  163. intentkit/skills/dune_analytics/dune.png +0 -0
  164. intentkit/skills/dune_analytics/fetch_kol_buys.py +128 -0
  165. intentkit/skills/dune_analytics/fetch_nation_metrics.py +237 -0
  166. intentkit/skills/dune_analytics/schema.json +99 -0
  167. intentkit/skills/elfa/README.md +100 -0
  168. intentkit/skills/elfa/__init__.py +123 -0
  169. intentkit/skills/elfa/base.py +28 -0
  170. intentkit/skills/elfa/elfa.jpg +0 -0
  171. intentkit/skills/elfa/mention.py +504 -0
  172. intentkit/skills/elfa/schema.json +153 -0
  173. intentkit/skills/elfa/stats.py +118 -0
  174. intentkit/skills/elfa/tokens.py +126 -0
  175. intentkit/skills/enso/README.md +75 -0
  176. intentkit/skills/enso/__init__.py +114 -0
  177. intentkit/skills/enso/abi/__init__.py +0 -0
  178. intentkit/skills/enso/abi/approval.py +279 -0
  179. intentkit/skills/enso/abi/erc20.py +14 -0
  180. intentkit/skills/enso/abi/route.py +129 -0
  181. intentkit/skills/enso/base.py +44 -0
  182. intentkit/skills/enso/best_yield.py +286 -0
  183. intentkit/skills/enso/enso.jpg +0 -0
  184. intentkit/skills/enso/networks.py +105 -0
  185. intentkit/skills/enso/prices.py +93 -0
  186. intentkit/skills/enso/route.py +300 -0
  187. intentkit/skills/enso/schema.json +212 -0
  188. intentkit/skills/enso/tokens.py +223 -0
  189. intentkit/skills/enso/wallet.py +381 -0
  190. intentkit/skills/github/README.md +63 -0
  191. intentkit/skills/github/__init__.py +54 -0
  192. intentkit/skills/github/base.py +21 -0
  193. intentkit/skills/github/github.jpg +0 -0
  194. intentkit/skills/github/github_search.py +183 -0
  195. intentkit/skills/github/schema.json +59 -0
  196. intentkit/skills/heurist/__init__.py +143 -0
  197. intentkit/skills/heurist/base.py +26 -0
  198. intentkit/skills/heurist/heurist.png +0 -0
  199. intentkit/skills/heurist/image_generation_animagine_xl.py +162 -0
  200. intentkit/skills/heurist/image_generation_arthemy_comics.py +162 -0
  201. intentkit/skills/heurist/image_generation_arthemy_real.py +162 -0
  202. intentkit/skills/heurist/image_generation_braindance.py +162 -0
  203. intentkit/skills/heurist/image_generation_cyber_realistic_xl.py +162 -0
  204. intentkit/skills/heurist/image_generation_flux_1_dev.py +162 -0
  205. intentkit/skills/heurist/image_generation_sdxl.py +161 -0
  206. intentkit/skills/heurist/schema.json +196 -0
  207. intentkit/skills/lifi/README.md +294 -0
  208. intentkit/skills/lifi/__init__.py +141 -0
  209. intentkit/skills/lifi/base.py +21 -0
  210. intentkit/skills/lifi/lifi.png +0 -0
  211. intentkit/skills/lifi/schema.json +89 -0
  212. intentkit/skills/lifi/token_execute.py +472 -0
  213. intentkit/skills/lifi/token_quote.py +190 -0
  214. intentkit/skills/lifi/utils.py +656 -0
  215. intentkit/skills/moralis/README.md +490 -0
  216. intentkit/skills/moralis/__init__.py +110 -0
  217. intentkit/skills/moralis/api.py +281 -0
  218. intentkit/skills/moralis/base.py +55 -0
  219. intentkit/skills/moralis/fetch_chain_portfolio.py +191 -0
  220. intentkit/skills/moralis/fetch_nft_portfolio.py +284 -0
  221. intentkit/skills/moralis/fetch_solana_portfolio.py +331 -0
  222. intentkit/skills/moralis/fetch_wallet_portfolio.py +301 -0
  223. intentkit/skills/moralis/moralis.png +0 -0
  224. intentkit/skills/moralis/schema.json +156 -0
  225. intentkit/skills/moralis/tests/__init__.py +0 -0
  226. intentkit/skills/moralis/tests/test_wallet.py +511 -0
  227. intentkit/skills/nation/__init__.py +62 -0
  228. intentkit/skills/nation/base.py +31 -0
  229. intentkit/skills/nation/nation.png +0 -0
  230. intentkit/skills/nation/nft_check.py +106 -0
  231. intentkit/skills/nation/schema.json +58 -0
  232. intentkit/skills/openai/__init__.py +107 -0
  233. intentkit/skills/openai/base.py +32 -0
  234. intentkit/skills/openai/dalle_image_generation.py +128 -0
  235. intentkit/skills/openai/gpt_image_generation.py +152 -0
  236. intentkit/skills/openai/gpt_image_to_image.py +186 -0
  237. intentkit/skills/openai/image_to_text.py +126 -0
  238. intentkit/skills/openai/openai.png +0 -0
  239. intentkit/skills/openai/schema.json +139 -0
  240. intentkit/skills/portfolio/README.md +55 -0
  241. intentkit/skills/portfolio/__init__.py +151 -0
  242. intentkit/skills/portfolio/base.py +107 -0
  243. intentkit/skills/portfolio/constants.py +9 -0
  244. intentkit/skills/portfolio/moralis.png +0 -0
  245. intentkit/skills/portfolio/schema.json +237 -0
  246. intentkit/skills/portfolio/token_balances.py +155 -0
  247. intentkit/skills/portfolio/wallet_approvals.py +102 -0
  248. intentkit/skills/portfolio/wallet_defi_positions.py +80 -0
  249. intentkit/skills/portfolio/wallet_history.py +155 -0
  250. intentkit/skills/portfolio/wallet_net_worth.py +112 -0
  251. intentkit/skills/portfolio/wallet_nfts.py +139 -0
  252. intentkit/skills/portfolio/wallet_profitability.py +101 -0
  253. intentkit/skills/portfolio/wallet_profitability_summary.py +91 -0
  254. intentkit/skills/portfolio/wallet_stats.py +79 -0
  255. intentkit/skills/portfolio/wallet_swaps.py +147 -0
  256. intentkit/skills/skills.toml +103 -0
  257. intentkit/skills/slack/__init__.py +98 -0
  258. intentkit/skills/slack/base.py +55 -0
  259. intentkit/skills/slack/get_channel.py +109 -0
  260. intentkit/skills/slack/get_message.py +136 -0
  261. intentkit/skills/slack/schedule_message.py +92 -0
  262. intentkit/skills/slack/schema.json +135 -0
  263. intentkit/skills/slack/send_message.py +81 -0
  264. intentkit/skills/slack/slack.jpg +0 -0
  265. intentkit/skills/system/__init__.py +90 -0
  266. intentkit/skills/system/base.py +22 -0
  267. intentkit/skills/system/read_agent_api_key.py +87 -0
  268. intentkit/skills/system/regenerate_agent_api_key.py +77 -0
  269. intentkit/skills/system/schema.json +53 -0
  270. intentkit/skills/system/system.svg +76 -0
  271. intentkit/skills/tavily/README.md +86 -0
  272. intentkit/skills/tavily/__init__.py +91 -0
  273. intentkit/skills/tavily/base.py +27 -0
  274. intentkit/skills/tavily/schema.json +119 -0
  275. intentkit/skills/tavily/tavily.jpg +0 -0
  276. intentkit/skills/tavily/tavily_extract.py +147 -0
  277. intentkit/skills/tavily/tavily_search.py +139 -0
  278. intentkit/skills/token/README.md +89 -0
  279. intentkit/skills/token/__init__.py +107 -0
  280. intentkit/skills/token/base.py +154 -0
  281. intentkit/skills/token/constants.py +9 -0
  282. intentkit/skills/token/erc20_transfers.py +145 -0
  283. intentkit/skills/token/moralis.png +0 -0
  284. intentkit/skills/token/schema.json +141 -0
  285. intentkit/skills/token/token_analytics.py +81 -0
  286. intentkit/skills/token/token_price.py +132 -0
  287. intentkit/skills/token/token_search.py +121 -0
  288. intentkit/skills/twitter/__init__.py +146 -0
  289. intentkit/skills/twitter/base.py +68 -0
  290. intentkit/skills/twitter/follow_user.py +69 -0
  291. intentkit/skills/twitter/get_mentions.py +124 -0
  292. intentkit/skills/twitter/get_timeline.py +111 -0
  293. intentkit/skills/twitter/get_user_by_username.py +84 -0
  294. intentkit/skills/twitter/get_user_tweets.py +123 -0
  295. intentkit/skills/twitter/like_tweet.py +65 -0
  296. intentkit/skills/twitter/post_tweet.py +90 -0
  297. intentkit/skills/twitter/reply_tweet.py +98 -0
  298. intentkit/skills/twitter/retweet.py +76 -0
  299. intentkit/skills/twitter/schema.json +258 -0
  300. intentkit/skills/twitter/search_tweets.py +115 -0
  301. intentkit/skills/twitter/twitter.png +0 -0
  302. intentkit/skills/unrealspeech/__init__.py +55 -0
  303. intentkit/skills/unrealspeech/base.py +21 -0
  304. intentkit/skills/unrealspeech/schema.json +100 -0
  305. intentkit/skills/unrealspeech/text_to_speech.py +177 -0
  306. intentkit/skills/unrealspeech/unrealspeech.jpg +0 -0
  307. intentkit/skills/venice_audio/__init__.py +106 -0
  308. intentkit/skills/venice_audio/base.py +119 -0
  309. intentkit/skills/venice_audio/input.py +41 -0
  310. intentkit/skills/venice_audio/schema.json +152 -0
  311. intentkit/skills/venice_audio/venice_audio.py +240 -0
  312. intentkit/skills/venice_audio/venice_logo.jpg +0 -0
  313. intentkit/skills/venice_image/README.md +119 -0
  314. intentkit/skills/venice_image/__init__.py +154 -0
  315. intentkit/skills/venice_image/api.py +138 -0
  316. intentkit/skills/venice_image/base.py +188 -0
  317. intentkit/skills/venice_image/config.py +35 -0
  318. intentkit/skills/venice_image/image_enhance/README.md +119 -0
  319. intentkit/skills/venice_image/image_enhance/__init__.py +0 -0
  320. intentkit/skills/venice_image/image_enhance/image_enhance.py +80 -0
  321. intentkit/skills/venice_image/image_enhance/image_enhance_base.py +23 -0
  322. intentkit/skills/venice_image/image_enhance/image_enhance_input.py +40 -0
  323. intentkit/skills/venice_image/image_generation/README.md +144 -0
  324. intentkit/skills/venice_image/image_generation/__init__.py +0 -0
  325. intentkit/skills/venice_image/image_generation/image_generation_base.py +117 -0
  326. intentkit/skills/venice_image/image_generation/image_generation_fluently_xl.py +26 -0
  327. intentkit/skills/venice_image/image_generation/image_generation_flux_dev.py +27 -0
  328. intentkit/skills/venice_image/image_generation/image_generation_flux_dev_uncensored.py +26 -0
  329. intentkit/skills/venice_image/image_generation/image_generation_input.py +158 -0
  330. intentkit/skills/venice_image/image_generation/image_generation_lustify_sdxl.py +26 -0
  331. intentkit/skills/venice_image/image_generation/image_generation_pony_realism.py +26 -0
  332. intentkit/skills/venice_image/image_generation/image_generation_stable_diffusion_3_5.py +28 -0
  333. intentkit/skills/venice_image/image_generation/image_generation_venice_sd35.py +28 -0
  334. intentkit/skills/venice_image/image_upscale/README.md +111 -0
  335. intentkit/skills/venice_image/image_upscale/__init__.py +0 -0
  336. intentkit/skills/venice_image/image_upscale/image_upscale.py +90 -0
  337. intentkit/skills/venice_image/image_upscale/image_upscale_base.py +23 -0
  338. intentkit/skills/venice_image/image_upscale/image_upscale_input.py +22 -0
  339. intentkit/skills/venice_image/image_vision/README.md +112 -0
  340. intentkit/skills/venice_image/image_vision/__init__.py +0 -0
  341. intentkit/skills/venice_image/image_vision/image_vision.py +100 -0
  342. intentkit/skills/venice_image/image_vision/image_vision_base.py +17 -0
  343. intentkit/skills/venice_image/image_vision/image_vision_input.py +9 -0
  344. intentkit/skills/venice_image/schema.json +267 -0
  345. intentkit/skills/venice_image/utils.py +78 -0
  346. intentkit/skills/venice_image/venice_image.jpg +0 -0
  347. intentkit/skills/web_scraper/README.md +82 -0
  348. intentkit/skills/web_scraper/__init__.py +92 -0
  349. intentkit/skills/web_scraper/base.py +21 -0
  350. intentkit/skills/web_scraper/langchain.png +0 -0
  351. intentkit/skills/web_scraper/schema.json +115 -0
  352. intentkit/skills/web_scraper/scrape_and_index.py +327 -0
  353. intentkit/utils/__init__.py +1 -0
  354. intentkit/utils/chain.py +436 -0
  355. intentkit/utils/error.py +134 -0
  356. intentkit/utils/logging.py +70 -0
  357. intentkit/utils/middleware.py +61 -0
  358. intentkit/utils/random.py +16 -0
  359. intentkit/utils/s3.py +267 -0
  360. intentkit/utils/slack_alert.py +79 -0
  361. intentkit/utils/tx.py +37 -0
  362. {intentkit-0.5.0.dist-info → intentkit-0.5.2.dist-info}/METADATA +1 -1
  363. intentkit-0.5.2.dist-info/RECORD +365 -0
  364. intentkit-0.5.0.dist-info/RECORD +0 -4
  365. {intentkit-0.5.0.dist-info → intentkit-0.5.2.dist-info}/WHEEL +0 -0
  366. {intentkit-0.5.0.dist-info → intentkit-0.5.2.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,472 @@
1
+ import asyncio
2
+ from typing import Any, Dict, List, Optional, Type
3
+
4
+ import httpx
5
+ from langchain_core.runnables import RunnableConfig
6
+ from pydantic import BaseModel, Field
7
+ from web3 import Web3
8
+
9
+ from intentkit.abstracts.skill import SkillStoreABC
10
+ from intentkit.clients import get_cdp_client
11
+ from intentkit.skills.lifi.base import LiFiBaseTool
12
+ from intentkit.skills.lifi.token_quote import TokenQuote
13
+ from intentkit.skills.lifi.utils import (
14
+ ERC20_ABI,
15
+ LIFI_API_URL,
16
+ build_quote_params,
17
+ convert_chain_to_id,
18
+ create_erc20_approve_data,
19
+ format_amount,
20
+ format_transaction_result,
21
+ handle_api_response,
22
+ is_native_token,
23
+ prepare_transaction_params,
24
+ validate_inputs,
25
+ )
26
+
27
+
28
+ class TokenExecuteInput(BaseModel):
29
+ """Input for the TokenExecute skill."""
30
+
31
+ from_chain: str = Field(
32
+ description="The source chain (e.g., 'ETH', 'POL', 'ARB', 'DAI'). Can be chain ID or chain key."
33
+ )
34
+ to_chain: str = Field(
35
+ description="The destination chain (e.g., 'ETH', 'POL', 'ARB', 'DAI'). Can be chain ID or chain key."
36
+ )
37
+ from_token: str = Field(
38
+ description="The token to send (e.g., 'USDC', 'ETH', 'DAI'). Can be token address or symbol."
39
+ )
40
+ to_token: str = Field(
41
+ description="The token to receive (e.g., 'USDC', 'ETH', 'DAI'). Can be token address or symbol."
42
+ )
43
+ from_amount: str = Field(
44
+ description="The amount to send, including all decimals (e.g., '1000000' for 1 USDC with 6 decimals)."
45
+ )
46
+ slippage: float = Field(
47
+ default=0.03,
48
+ description="Maximum acceptable slippage as a decimal (e.g., 0.03 for 3%). Default is 3%.",
49
+ )
50
+
51
+
52
+ class TokenExecute(LiFiBaseTool):
53
+ """Tool for executing token transfers across chains using LiFi.
54
+
55
+ This tool executes actual token transfers and swaps using the CDP wallet provider.
56
+ Requires a properly configured CDP wallet to work.
57
+ """
58
+
59
+ name: str = "lifi_token_execute"
60
+ description: str = (
61
+ "Execute a token transfer across blockchains or swap tokens on the same chain.\n"
62
+ "This requires a CDP wallet with sufficient funds and proper network configuration.\n"
63
+ "Use token_quote first to check rates and fees before executing.\n"
64
+ "Supports all major chains like Ethereum, Polygon, Arbitrum, Optimism, Base, and more."
65
+ )
66
+ args_schema: Type[BaseModel] = TokenExecuteInput
67
+ api_url: str = LIFI_API_URL
68
+
69
+ # Configuration options
70
+ default_slippage: float = 0.03
71
+ allowed_chains: Optional[List[str]] = None
72
+ max_execution_time: int = 300
73
+ quote_tool: TokenQuote = Field(default=None, exclude=True)
74
+
75
+ def __init__(
76
+ self,
77
+ skill_store: SkillStoreABC,
78
+ default_slippage: float = 0.03,
79
+ allowed_chains: Optional[List[str]] = None,
80
+ max_execution_time: int = 300,
81
+ ):
82
+ """Initialize the TokenExecute skill with configuration options."""
83
+ super().__init__(skill_store=skill_store)
84
+ self.default_slippage = default_slippage
85
+ self.allowed_chains = allowed_chains
86
+ self.max_execution_time = max_execution_time
87
+ # Initialize quote tool if not set
88
+ if not self.quote_tool:
89
+ self.quote_tool = TokenQuote(
90
+ skill_store=skill_store,
91
+ default_slippage=default_slippage,
92
+ allowed_chains=allowed_chains,
93
+ )
94
+
95
+ def _format_quote_result(self, data: Dict[str, Any]) -> str:
96
+ """Format the quote result in a readable format."""
97
+ # Use the same formatting as token_quote
98
+ return self.quote_tool._format_quote_result(data)
99
+
100
+ async def _arun(
101
+ self,
102
+ config: RunnableConfig,
103
+ from_chain: str,
104
+ to_chain: str,
105
+ from_token: str,
106
+ to_token: str,
107
+ from_amount: str,
108
+ slippage: float = None,
109
+ **kwargs,
110
+ ) -> str:
111
+ """Execute a token transfer."""
112
+ try:
113
+ # Use provided slippage or default
114
+ if slippage is None:
115
+ slippage = self.default_slippage
116
+
117
+ # Validate all inputs
118
+ validation_error = validate_inputs(
119
+ from_chain,
120
+ to_chain,
121
+ from_token,
122
+ to_token,
123
+ from_amount,
124
+ slippage,
125
+ self.allowed_chains,
126
+ )
127
+ if validation_error:
128
+ return validation_error
129
+
130
+ # Get agent context for CDP wallet
131
+ context = self.context_from_config(config)
132
+ agent_id = context.agent.id
133
+
134
+ self.logger.info(
135
+ f"Executing LiFi transfer: {from_amount} {from_token} on {from_chain} -> {to_token} on {to_chain}"
136
+ )
137
+
138
+ # Get CDP wallet provider
139
+ cdp_wallet_provider = await self._get_cdp_wallet_provider(agent_id)
140
+ if isinstance(cdp_wallet_provider, str): # Error message
141
+ return cdp_wallet_provider
142
+
143
+ # Get wallet address
144
+ from_address = cdp_wallet_provider.get_address()
145
+ if not from_address:
146
+ return "No wallet address available. Please check your CDP wallet configuration."
147
+
148
+ # Get quote and execute transfer
149
+ async with httpx.AsyncClient() as client:
150
+ # Step 1: Get quote
151
+ quote_data = await self._get_quote(
152
+ client,
153
+ from_chain,
154
+ to_chain,
155
+ from_token,
156
+ to_token,
157
+ from_amount,
158
+ slippage,
159
+ from_address,
160
+ )
161
+ if isinstance(quote_data, str): # Error message
162
+ return quote_data
163
+
164
+ # Step 2: Handle token approval if needed
165
+ approval_result = await self._handle_token_approval(
166
+ cdp_wallet_provider, quote_data
167
+ )
168
+ if approval_result:
169
+ self.logger.info(f"Token approval completed: {approval_result}")
170
+
171
+ # Step 3: Execute transaction
172
+ tx_hash = await self._execute_transfer_transaction(
173
+ cdp_wallet_provider, quote_data
174
+ )
175
+
176
+ # Step 4: Monitor status and return result
177
+ return await self._finalize_transfer(
178
+ client, tx_hash, from_chain, to_chain, quote_data
179
+ )
180
+
181
+ except Exception as e:
182
+ self.logger.error("LiFi_Error: %s", str(e))
183
+ return f"An unexpected error occurred: {str(e)}"
184
+
185
+ async def _get_cdp_wallet_provider(self, agent_id: str):
186
+ """Get CDP wallet provider with error handling."""
187
+ try:
188
+ cdp_client = await get_cdp_client(agent_id, self.skill_store)
189
+ if not cdp_client:
190
+ return "CDP client not available. Please ensure your agent has CDP wallet configuration."
191
+
192
+ cdp_wallet_provider = await cdp_client.get_wallet_provider()
193
+ if not cdp_wallet_provider:
194
+ return "CDP wallet provider not configured. Please set up your agent's CDP wallet first."
195
+
196
+ return cdp_wallet_provider
197
+
198
+ except Exception as e:
199
+ self.logger.error("LiFi_CDP_Error: %s", str(e))
200
+ return f"Cannot access CDP wallet: {str(e)}\n\nPlease ensure your agent has a properly configured CDP wallet with sufficient funds."
201
+
202
+ async def _get_quote(
203
+ self,
204
+ client: httpx.AsyncClient,
205
+ from_chain: str,
206
+ to_chain: str,
207
+ from_token: str,
208
+ to_token: str,
209
+ from_amount: str,
210
+ slippage: float,
211
+ from_address: str,
212
+ ) -> Dict[str, Any]:
213
+ """Get quote from LiFi API."""
214
+ api_params = build_quote_params(
215
+ from_chain,
216
+ to_chain,
217
+ from_token,
218
+ to_token,
219
+ from_amount,
220
+ slippage,
221
+ from_address,
222
+ )
223
+
224
+ try:
225
+ response = await client.get(
226
+ f"{self.api_url}/quote",
227
+ params=api_params,
228
+ timeout=30.0,
229
+ )
230
+ except httpx.TimeoutException:
231
+ return "Request timed out. The LiFi service might be temporarily unavailable. Please try again."
232
+ except httpx.ConnectError:
233
+ return "Connection error. Unable to reach LiFi service. Please check your internet connection."
234
+ except Exception as e:
235
+ self.logger.error("LiFi_API_Error: %s", str(e))
236
+ return f"Error making API request: {str(e)}"
237
+
238
+ # Handle response
239
+ data, error = handle_api_response(
240
+ response, from_token, from_chain, to_token, to_chain
241
+ )
242
+ if error:
243
+ self.logger.error("LiFi_API_Error: %s", error)
244
+ return error
245
+
246
+ # Validate transaction request
247
+ transaction_request = data.get("transactionRequest")
248
+ if not transaction_request:
249
+ return "No transaction request found in the quote. Cannot execute transfer."
250
+
251
+ return data
252
+
253
+ async def _handle_token_approval(
254
+ self, wallet_provider, quote_data: Dict[str, Any]
255
+ ) -> Optional[str]:
256
+ """Handle ERC20 token approval if needed."""
257
+ estimate = quote_data.get("estimate", {})
258
+ approval_address = estimate.get("approvalAddress")
259
+ from_token_info = quote_data.get("action", {}).get("fromToken", {})
260
+ from_token_address = from_token_info.get("address", "")
261
+ from_amount = quote_data.get("action", {}).get("fromAmount", "0")
262
+
263
+ # Skip approval for native tokens
264
+ if is_native_token(from_token_address) or not approval_address:
265
+ return None
266
+
267
+ self.logger.info("Checking token approval for ERC20 transfer...")
268
+
269
+ try:
270
+ return await self._check_and_set_allowance(
271
+ wallet_provider, from_token_address, approval_address, from_amount
272
+ )
273
+ except Exception as e:
274
+ self.logger.error("LiFi_Token_Approval_Error: %s", str(e))
275
+ raise Exception(f"Failed to approve token: {str(e)}")
276
+
277
+ async def _execute_transfer_transaction(
278
+ self, wallet_provider, quote_data: Dict[str, Any]
279
+ ) -> str:
280
+ """Execute the main transfer transaction."""
281
+ transaction_request = quote_data.get("transactionRequest")
282
+
283
+ try:
284
+ tx_params = prepare_transaction_params(transaction_request)
285
+ self.logger.info(
286
+ f"Sending transaction to {tx_params['to']} with value {tx_params['value']}"
287
+ )
288
+
289
+ # Send transaction
290
+ tx_hash = wallet_provider.send_transaction(tx_params)
291
+
292
+ # Wait for confirmation
293
+ receipt = wallet_provider.wait_for_transaction_receipt(tx_hash)
294
+ if not receipt or receipt.get("status") == 0:
295
+ raise Exception(f"Transaction failed: {tx_hash}")
296
+
297
+ return tx_hash
298
+
299
+ except Exception as e:
300
+ self.logger.error("LiFi_Execution_Error: %s", str(e))
301
+ raise Exception(f"Failed to execute transaction: {str(e)}")
302
+
303
+ async def _finalize_transfer(
304
+ self,
305
+ client: httpx.AsyncClient,
306
+ tx_hash: str,
307
+ from_chain: str,
308
+ to_chain: str,
309
+ quote_data: Dict[str, Any],
310
+ ) -> str:
311
+ """Finalize transfer and return formatted result."""
312
+ self.logger.info(f"Transaction sent: {tx_hash}")
313
+
314
+ # Get chain ID for explorer URL
315
+ from_chain_id = convert_chain_to_id(from_chain)
316
+
317
+ # Extract token info for result formatting
318
+ action = quote_data.get("action", {})
319
+ from_token_info = action.get("fromToken", {})
320
+ to_token_info = action.get("toToken", {})
321
+
322
+ token_info = {
323
+ "symbol": f"{from_token_info.get('symbol', 'Unknown')} → {to_token_info.get('symbol', 'Unknown')}",
324
+ "amount": format_amount(
325
+ action.get("fromAmount", "0"), from_token_info.get("decimals", 18)
326
+ ),
327
+ }
328
+
329
+ # Format transaction result with explorer URL
330
+ transaction_result = format_transaction_result(
331
+ tx_hash, from_chain_id, token_info
332
+ )
333
+
334
+ # Format quote details
335
+ formatted_quote = self._format_quote_result(quote_data)
336
+
337
+ # Handle cross-chain vs same-chain transfers
338
+ if from_chain.lower() != to_chain.lower():
339
+ self.logger.info("Monitoring cross-chain transfer status...")
340
+ status_result = await self._monitor_transfer_status(
341
+ client, tx_hash, from_chain, to_chain
342
+ )
343
+
344
+ return f"""**Token Transfer Executed Successfully**
345
+
346
+ {transaction_result}
347
+ {status_result}
348
+
349
+ {formatted_quote}
350
+ """
351
+ else:
352
+ return f"""**Token Swap Executed Successfully**
353
+
354
+ {transaction_result}
355
+ **Status:** Completed (same-chain swap)
356
+
357
+ {formatted_quote}
358
+ """
359
+
360
+ async def _monitor_transfer_status(
361
+ self, client: httpx.AsyncClient, tx_hash: str, from_chain: str, to_chain: str
362
+ ) -> str:
363
+ """Monitor the status of a cross-chain transfer."""
364
+ max_attempts = min(self.max_execution_time // 10, 30) # Check every 10 seconds
365
+ attempt = 0
366
+
367
+ while attempt < max_attempts:
368
+ try:
369
+ status_response = await client.get(
370
+ f"{self.api_url}/status",
371
+ params={
372
+ "txHash": tx_hash,
373
+ "fromChain": from_chain,
374
+ "toChain": to_chain,
375
+ },
376
+ timeout=10.0,
377
+ )
378
+
379
+ if status_response.status_code == 200:
380
+ status_data = status_response.json()
381
+ status = status_data.get("status", "UNKNOWN")
382
+
383
+ if status == "DONE":
384
+ receiving_tx = status_data.get("receiving", {}).get("txHash")
385
+ if receiving_tx:
386
+ return (
387
+ f"**Status:** Complete (destination tx: {receiving_tx})"
388
+ )
389
+ else:
390
+ return "**Status:** Complete"
391
+ elif status == "FAILED":
392
+ return "**Status:** Failed"
393
+ elif status in ["PENDING", "NOT_FOUND"]:
394
+ # Continue monitoring
395
+ pass
396
+ else:
397
+ return f"**Status:** {status}"
398
+
399
+ except Exception as e:
400
+ self.logger.warning(
401
+ f"Status check failed (attempt {attempt + 1}): {str(e)}"
402
+ )
403
+
404
+ attempt += 1
405
+ if attempt < max_attempts:
406
+ await asyncio.sleep(10) # Wait 10 seconds before next check
407
+
408
+ return "**Status:** Processing (monitoring timed out, but transfer may still complete)"
409
+
410
+ async def _check_and_set_allowance(
411
+ self,
412
+ wallet_provider,
413
+ token_address: str,
414
+ approval_address: str,
415
+ amount: str,
416
+ ) -> Optional[str]:
417
+ """Check if token allowance is sufficient and set approval if needed."""
418
+ try:
419
+ # Normalize addresses
420
+ token_address = Web3.to_checksum_address(token_address)
421
+ approval_address = Web3.to_checksum_address(approval_address)
422
+ wallet_address = wallet_provider.get_address()
423
+
424
+ # Check current allowance
425
+ try:
426
+ current_allowance = wallet_provider.read_contract(
427
+ contract_address=token_address,
428
+ abi=ERC20_ABI,
429
+ function_name="allowance",
430
+ args=[wallet_address, approval_address],
431
+ )
432
+
433
+ required_amount = int(amount)
434
+
435
+ if current_allowance >= required_amount:
436
+ self.logger.info(
437
+ f"Sufficient allowance already exists: {current_allowance}"
438
+ )
439
+ return None # No approval needed
440
+
441
+ except Exception as e:
442
+ self.logger.warning(f"Could not check current allowance: {str(e)}")
443
+ # Continue with approval anyway
444
+
445
+ # Set approval for the required amount
446
+ self.logger.info(
447
+ f"Setting token approval for {amount} tokens to {approval_address}"
448
+ )
449
+
450
+ # Create approval transaction
451
+ approve_data = create_erc20_approve_data(approval_address, amount)
452
+
453
+ # Send approval transaction
454
+ approval_tx_hash = wallet_provider.send_transaction(
455
+ {
456
+ "to": token_address,
457
+ "data": approve_data,
458
+ "value": 0,
459
+ }
460
+ )
461
+
462
+ # Wait for approval transaction confirmation
463
+ receipt = wallet_provider.wait_for_transaction_receipt(approval_tx_hash)
464
+
465
+ if not receipt or receipt.get("status") == 0:
466
+ raise Exception(f"Approval transaction failed: {approval_tx_hash}")
467
+
468
+ return approval_tx_hash
469
+
470
+ except Exception as e:
471
+ self.logger.error(f"Token approval failed: {str(e)}")
472
+ raise Exception(f"Failed to approve token transfer: {str(e)}")
@@ -0,0 +1,190 @@
1
+ from typing import Any, Dict, List, Optional, Type
2
+
3
+ import httpx
4
+ from langchain_core.runnables import RunnableConfig
5
+ from pydantic import BaseModel, Field
6
+
7
+ from intentkit.abstracts.skill import SkillStoreABC
8
+ from intentkit.skills.lifi.base import LiFiBaseTool
9
+ from intentkit.skills.lifi.utils import (
10
+ LIFI_API_URL,
11
+ build_quote_params,
12
+ format_duration,
13
+ format_fees_and_gas,
14
+ format_quote_basic_info,
15
+ format_route_info,
16
+ handle_api_response,
17
+ validate_inputs,
18
+ )
19
+
20
+
21
+ class TokenQuoteInput(BaseModel):
22
+ """Input for the TokenQuote skill."""
23
+
24
+ from_chain: str = Field(
25
+ description="The source chain (e.g., 'ETH', 'POL', 'ARB', 'DAI'). Can be chain ID or chain key."
26
+ )
27
+ to_chain: str = Field(
28
+ description="The destination chain (e.g., 'ETH', 'POL', 'ARB', 'DAI'). Can be chain ID or chain key."
29
+ )
30
+ from_token: str = Field(
31
+ description="The token to send (e.g., 'USDC', 'ETH', 'DAI'). Can be token address or symbol."
32
+ )
33
+ to_token: str = Field(
34
+ description="The token to receive (e.g., 'USDC', 'ETH', 'DAI'). Can be token address or symbol."
35
+ )
36
+ from_amount: str = Field(
37
+ description="The amount to send, including all decimals (e.g., '1000000' for 1 USDC with 6 decimals)."
38
+ )
39
+ slippage: float = Field(
40
+ default=0.03,
41
+ description="The maximum allowed slippage for the transaction (0.03 represents 3%).",
42
+ )
43
+
44
+
45
+ class TokenQuote(LiFiBaseTool):
46
+ """Tool for getting token transfer quotes across chains using LiFi.
47
+
48
+ This tool provides quotes for token transfers and swaps without executing transactions.
49
+ """
50
+
51
+ name: str = "lifi_token_quote"
52
+ description: str = (
53
+ "Get a quote for transferring tokens across blockchains or swapping tokens.\n"
54
+ "Use this tool to check rates, fees, and estimated time for token transfers without executing them.\n"
55
+ "Supports all major chains like Ethereum, Polygon, Arbitrum, Optimism, Base, and more."
56
+ )
57
+ args_schema: Type[BaseModel] = TokenQuoteInput
58
+ api_url: str = LIFI_API_URL
59
+
60
+ # Configuration options
61
+ default_slippage: float = 0.03
62
+ allowed_chains: Optional[List[str]] = None
63
+
64
+ def __init__(
65
+ self,
66
+ skill_store: SkillStoreABC,
67
+ default_slippage: float = 0.03,
68
+ allowed_chains: Optional[List[str]] = None,
69
+ ):
70
+ """Initialize the TokenQuote skill with configuration options."""
71
+ super().__init__(skill_store=skill_store)
72
+ self.default_slippage = default_slippage
73
+ self.allowed_chains = allowed_chains
74
+
75
+ async def _arun(
76
+ self,
77
+ config: RunnableConfig,
78
+ from_chain: str,
79
+ to_chain: str,
80
+ from_token: str,
81
+ to_token: str,
82
+ from_amount: str,
83
+ slippage: float = None,
84
+ **kwargs,
85
+ ) -> str:
86
+ """Get a quote for token transfer."""
87
+ try:
88
+ # Use provided slippage or default
89
+ if slippage is None:
90
+ slippage = self.default_slippage
91
+
92
+ # Validate all inputs
93
+ validation_error = validate_inputs(
94
+ from_chain,
95
+ to_chain,
96
+ from_token,
97
+ to_token,
98
+ from_amount,
99
+ slippage,
100
+ self.allowed_chains,
101
+ )
102
+ if validation_error:
103
+ return validation_error
104
+
105
+ self.logger.info(
106
+ f"Requesting LiFi quote: {from_amount} {from_token} on {from_chain} -> {to_token} on {to_chain}"
107
+ )
108
+
109
+ # Build API parameters
110
+ api_params = build_quote_params(
111
+ from_chain, to_chain, from_token, to_token, from_amount, slippage
112
+ )
113
+
114
+ # Make API request
115
+ async with httpx.AsyncClient() as client:
116
+ try:
117
+ response = await client.get(
118
+ f"{self.api_url}/quote",
119
+ params=api_params,
120
+ timeout=30.0,
121
+ )
122
+ except httpx.TimeoutException:
123
+ return "Request timed out. The LiFi service might be temporarily unavailable. Please try again."
124
+ except httpx.ConnectError:
125
+ return "Connection error. Unable to reach LiFi service. Please check your internet connection."
126
+ except Exception as e:
127
+ self.logger.error("LiFi_API_Error: %s", str(e))
128
+ return f"Error making API request: {str(e)}"
129
+
130
+ # Handle response
131
+ data, error = handle_api_response(
132
+ response, from_token, from_chain, to_token, to_chain
133
+ )
134
+ if error:
135
+ self.logger.error("LiFi_API_Error: %s", error)
136
+ return error
137
+
138
+ # Format the quote result
139
+ return self._format_quote_result(data)
140
+
141
+ except Exception as e:
142
+ self.logger.error("LiFi_Error: %s", str(e))
143
+ return f"An unexpected error occurred: {str(e)}"
144
+
145
+ def _format_quote_result(self, data: Dict[str, Any]) -> str:
146
+ """Format quote result into human-readable text."""
147
+ try:
148
+ # Get basic info
149
+ info = format_quote_basic_info(data)
150
+
151
+ # Build result string
152
+ result = "### Token Transfer Quote\n\n"
153
+ result += f"**From:** {info['from_amount']} {info['from_token']} on {info['from_chain']}\n"
154
+ result += f"**To:** {info['to_amount']} {info['to_token']} on {info['to_chain']}\n"
155
+ result += (
156
+ f"**Minimum Received:** {info['to_amount_min']} {info['to_token']}\n"
157
+ )
158
+ result += f"**Bridge/Exchange:** {info['tool']}\n\n"
159
+
160
+ # Add USD values if available
161
+ if info["from_amount_usd"] and info["to_amount_usd"]:
162
+ result += f"**Value:** ${info['from_amount_usd']} → ${info['to_amount_usd']}\n\n"
163
+
164
+ # Add execution time estimate
165
+ if info["execution_duration"]:
166
+ time_str = format_duration(info["execution_duration"])
167
+ result += f"**Estimated Time:** {time_str}\n\n"
168
+
169
+ # Add fees and gas costs
170
+ fees_text, gas_text = format_fees_and_gas(data)
171
+ if fees_text:
172
+ result += fees_text + "\n"
173
+ if gas_text:
174
+ result += gas_text + "\n"
175
+
176
+ # Add route information
177
+ route_text = format_route_info(data)
178
+ if route_text:
179
+ result += route_text + "\n"
180
+
181
+ result += "---\n"
182
+ result += (
183
+ "*Use token_execute to perform this transfer with your CDP wallet*"
184
+ )
185
+
186
+ return result
187
+
188
+ except Exception as e:
189
+ self.logger.error("Format_Error: %s", str(e))
190
+ return f"Quote received but formatting failed: {str(e)}\nRaw data: {str(data)[:500]}..."