Longy King commited on
Commit
c4ca6af
·
unverified ·
2 Parent(s): 3a5ee58 84ef220

Merge pull request #15 from chatxbt/fix/rpc-client-timeout

Browse files
Procfile CHANGED
@@ -1 +1 @@
1
- web: docker-compose up --build
 
1
+ web: docker-compose up --build
src/config/assistant.py ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ description = """
2
+ You are ChatXBT,
3
+ a web3 assistant developed and equipped to assist users with answering web3 questions,
4
+ web3 research, providing web3 solutions, and carrying out any web3-related tasks.
5
+ """
6
+
7
+ instruction = [
8
+ """
9
+ Scope of Conversation:
10
+ - This conversation is exclusively within the context and scope of web3,
11
+ decentralized finance (DeFi), blockchain technology, and cryptocurrency ecosystems.
12
+ """,
13
+ """
14
+ Handling Non-Related Queries:
15
+ - For any conversation outside the scope or context of web3, DeFi, blockchain technology,
16
+ or cryptocurrencies, inform the user that you are trained to assist only within the mentioned scopes above and provide a standard response.
17
+ - Standard response for out-of-scope queries: I'm sorry, but I can only assist with questions and tasks related to web3, decentralized finance (DeFi), blockchain technology, and cryptocurrencies.
18
+ """
19
+ ]
src/libs/rpc_client.py CHANGED
@@ -9,7 +9,8 @@ rpc_server_url = os.getenv('CHATXBT_RPC_SERVER_URL')
9
  async def rpc_call(
10
  method_name: str, # The name of the RPC method to be called
11
  params: Optional[Union[dict, list]] = None, # Optional parameters for the RPC method
12
- url: str = rpc_server_url # The URL of the RPC server
 
13
  ) -> dict: # Returns the JSON response from the RPC server
14
  """
15
  This function makes an RPC call to the specified URL with the given method name and parameters.
@@ -43,7 +44,7 @@ async def rpc_call(
43
  }
44
 
45
  try:
46
- async with httpx.AsyncClient() as client:
47
  response = await client.post(url, json=payload, headers=headers, auth=auth)
48
  response.raise_for_status()
49
  return response.json()
 
9
  async def rpc_call(
10
  method_name: str, # The name of the RPC method to be called
11
  params: Optional[Union[dict, list]] = None, # Optional parameters for the RPC method
12
+ url: str = rpc_server_url, # The URL of the RPC server
13
+ timeout: int = 60 # Timeout in seconds
14
  ) -> dict: # Returns the JSON response from the RPC server
15
  """
16
  This function makes an RPC call to the specified URL with the given method name and parameters.
 
44
  }
45
 
46
  try:
47
+ async with httpx.AsyncClient(timeout=timeout) as client:
48
  response = await client.post(url, json=payload, headers=headers, auth=auth)
49
  response.raise_for_status()
50
  return response.json()
src/tools/crypto_bridge_toolkit.py ADDED
@@ -0,0 +1,176 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import TypedDict, Any
2
+ import asyncio
3
+ from phi.tools import Toolkit
4
+ from phi.utils.log import logger
5
+ from src.libs.rpc_client import rpc_call
6
+
7
+ # Define TypedDict for wallet
8
+ class WalletParams(TypedDict):
9
+ userEmail: str
10
+ chain: str
11
+ testnet: bool
12
+ gasless: bool
13
+
14
+ # Define TypedDict for tokenIn and tokenOut (since they have identical structures)
15
+ class TokenParams(TypedDict):
16
+ chainId: int
17
+ address: str
18
+ decimals: int
19
+ symbol: str
20
+ name: str
21
+
22
+ class CrossChainSwapTools(Toolkit):
23
+ def __init__(self):
24
+ super().__init__(name="cross_chain_swap_tools")
25
+
26
+ # Registering methods to make them accessible via the toolkit
27
+ self.register(self.cross_chain_swap)
28
+ self.register(self.get_bridge_chains)
29
+ self.register(self.get_cross_chain_swap_token_list)
30
+
31
+ def cross_chain_swap(
32
+ self,
33
+ chainId: int,
34
+ amountLD: str,
35
+ recipient: str,
36
+ user_email: str,
37
+ tokenIn: str,
38
+ tokenOut: str,
39
+ chain: str = "base",
40
+ ) -> str:
41
+ """
42
+ Performs a cross-chain token swap. this is a sensitive function, show a preview and allow user to give final permission to execute function
43
+
44
+ Parameters:
45
+ - chainId (int): The ID of the chain.
46
+ - amountLD (str): The amount to be swapped, in lowest denomination (e.g., wei for ETH).
47
+ - recipient (str): The recipient address.
48
+ - user_email (str): The email of the user for whom the wallet is being fetched and used to sign the transaction.
49
+ - tokenIn (TokenParams): The input token parameters as structured from: get_cross_chain_swap_token_list. stringify object
50
+ - tokenOut (TokenParams): The output token parameters as structured from: get_cross_chain_swap_token_list. stringify object
51
+ - chain (str): The EVM chain for which the wallet is being fetched and used to execute this transaction. use chain base as default
52
+
53
+ Returns:
54
+ - str: A string representation of the response from the RPC call.
55
+
56
+ Raises:
57
+ None
58
+
59
+ Note:
60
+ This method uses `asyncio.run()` to run the asynchronous RPC call.
61
+ """
62
+ logger.info(f"Performing cross-chain swap on chain {chainId} for recipient {recipient}")
63
+
64
+ params = {
65
+ 'chainId': chainId,
66
+ 'amountLD': amountLD,
67
+ 'recipient': recipient,
68
+ 'wallet': {
69
+ "userEmail": user_email,
70
+ "chain": chain,
71
+ "testnet": True,
72
+ "gasless": True,
73
+ "connected": True
74
+ },
75
+ 'tokenIn': tokenIn,
76
+ 'tokenOut': tokenOut,
77
+ }
78
+ response = asyncio.run(rpc_call(method_name="crossChainSwap", params=params))
79
+ return self._format_response(response)
80
+
81
+ def get_bridge_chains(self, chainId: int) -> str:
82
+ """
83
+ Fetches the list of supported bridge chains.
84
+
85
+ Parameters:
86
+ - chainId (int): The ID of the chain. Supported values:
87
+ - 1: Ethereum
88
+ - 10: Optimism
89
+ - 56: BNB Chain
90
+ - 100: Gnosis Chain
91
+ - 137: Polygon
92
+ - 324: zkSync Era
93
+ - 1088: Metis
94
+ - 8453: Base
95
+ - 42161: Arbitrum
96
+ - 43114: Avalanche
97
+ """
98
+ logger.info(f"Fetching supported bridge chains for chain ID {chainId}")
99
+
100
+ params = {'chainId': chainId}
101
+ response = asyncio.run(rpc_call(method_name="getBridgeChains", params=params))
102
+ return self._format_response(response)
103
+
104
+ def get_cross_chain_swap_token_list(self, chainId: int) -> str:
105
+ """
106
+ Fetches the list of supported cross-chain swap tokens on the specified chainId. always call before executing
107
+ cross_chain_swap to get accurate tokenIn and tokenOut data
108
+
109
+ Parameters:
110
+ - chainId (int): The ID of the chain.
111
+
112
+ Returns:
113
+ - str: A string representation of the response from the RPC call containing the list of cryptocurrency tokens,
114
+ each with details about their source token and the corresponding destination
115
+ tokens on different blockchain networks.
116
+
117
+ Raises:
118
+ None
119
+
120
+ Note:
121
+ This method uses `asyncio.run()` to run the asynchronous RPC call.
122
+ present data in proper nice readable format
123
+ """
124
+ logger.info(f"Fetching cross-chain swap token list for chain ID {chainId}")
125
+
126
+ params = {'chainId': chainId}
127
+ response = asyncio.run(rpc_call(method_name="getCrossChainSwapTokenList", params=params))
128
+ return self._format_response(response)
129
+
130
+ def _format_response(self, response: dict) -> str:
131
+ """
132
+ Formats the response from the RPC call into a readable string.
133
+
134
+ Parameters:
135
+ - response (dict): The response from the RPC call.
136
+
137
+ Returns:
138
+ - str: A formatted string representation of the response.
139
+ """
140
+ if 'error' in response:
141
+ logger.error(f"RPC call failed with error: {response['error']}")
142
+ return f"Error: {response['error']}"
143
+ return f"Response: {response}"
144
+
145
+ # Example usage:
146
+ # toolkit = CrossChainSwapTools()
147
+ # print(toolkit.get_bridge_chains(1))
148
+ # print(toolkit.get_cross_chain_swap_token_list(1))
149
+ # swap_params = {
150
+ # "chainId": 1,
151
+ # "amountLD": "100",
152
+ # "recipient": "0xRecipientAddress",
153
+ # "wallet": {
154
+ # "userEmail": "user@example.com",
155
+ # "chain": "ethereum",
156
+ # "testnet": True,
157
+ # "gasless": True
158
+ # },
159
+ # "tokenIn": {
160
+ # "chainId": 1,
161
+ # "address": "0xInputTokenAddress",
162
+ # "decimals": 18,
163
+ # "symbol": "TOKEN_IN",
164
+ # "name": "Input Token",
165
+ # "logoUri": "https://example.com/logo_in.png"
166
+ # },
167
+ # "tokenOut": {
168
+ # "chainId": 137,
169
+ # "address": "0xOutputTokenAddress",
170
+ # "decimals": 18,
171
+ # "symbol": "TOKEN_OUT",
172
+ # "name": "Output Token",
173
+ # "logoUri": "https://example.com/logo_out.png"
174
+ # }
175
+ # }
176
+ # print(toolkit.cross_chain_swap(**swap_params))
src/tools/crypto_swap_toolkit.py CHANGED
@@ -26,7 +26,7 @@ class CryptoSwapTools(Toolkit):
26
  self.register(self.get_swap_sources)
27
  self.register(self.execute_swap)
28
 
29
- def get_swap_quote(self, buy_token: str, sell_token: str, sell_amount: str) -> str:
30
  """
31
  Fetches a swap quote from the 0x Swap API.
32
 
@@ -46,7 +46,14 @@ class CryptoSwapTools(Toolkit):
46
  params = {
47
  'buyToken': buy_token,
48
  'sellToken': sell_token,
49
- 'sellAmount': sell_amount
 
 
 
 
 
 
 
50
  }
51
  response = asyncio.run(rpc_call(method_name="getSwapQuote", params=params))
52
  return f"{response}"
@@ -55,7 +62,7 @@ class CryptoSwapTools(Toolkit):
55
  # return {"error": str(e)}
56
  return f"Error: {e}"
57
 
58
- def get_swap_price(self, buy_token: str, sell_token: str, buy_amount: str) -> str:
59
  """
60
  Fetches the price for a swap from the 0x Swap API.
61
 
@@ -75,7 +82,14 @@ class CryptoSwapTools(Toolkit):
75
  params = {
76
  'buyToken': buy_token,
77
  'sellToken': sell_token,
78
- 'buyAmount': buy_amount
 
 
 
 
 
 
 
79
  }
80
  response = asyncio.run(rpc_call(method_name="getSwapPrice", params=params))
81
  return f"{response}"
@@ -108,7 +122,7 @@ class CryptoSwapTools(Toolkit):
108
  chain: str = "base",
109
  ) -> str:
110
  """
111
- Executes a swap using the 0x Swap API.
112
 
113
  Args:
114
  -buy_token (str): The token to buy (e.g., 'DAI').
 
26
  self.register(self.get_swap_sources)
27
  self.register(self.execute_swap)
28
 
29
+ def get_swap_quote(self, buy_token: str, sell_token: str, sell_amount: str, chain: str = "base") -> str:
30
  """
31
  Fetches a swap quote from the 0x Swap API.
32
 
 
46
  params = {
47
  'buyToken': buy_token,
48
  'sellToken': sell_token,
49
+ 'sellAmount': sell_amount,
50
+ 'wallet': {
51
+ "userEmail": "none",
52
+ "chain": chain,
53
+ "testnet": True,
54
+ "gasless": True,
55
+ "connected": True
56
+ }
57
  }
58
  response = asyncio.run(rpc_call(method_name="getSwapQuote", params=params))
59
  return f"{response}"
 
62
  # return {"error": str(e)}
63
  return f"Error: {e}"
64
 
65
+ def get_swap_price(self, buy_token: str, sell_token: str, buy_amount: str, chain: str = "base") -> str:
66
  """
67
  Fetches the price for a swap from the 0x Swap API.
68
 
 
82
  params = {
83
  'buyToken': buy_token,
84
  'sellToken': sell_token,
85
+ 'buyAmount': buy_amount,
86
+ 'wallet': {
87
+ "userEmail": "none",
88
+ "chain": chain,
89
+ "testnet": True,
90
+ "gasless": True,
91
+ "connected": True
92
+ }
93
  }
94
  response = asyncio.run(rpc_call(method_name="getSwapPrice", params=params))
95
  return f"{response}"
 
122
  chain: str = "base",
123
  ) -> str:
124
  """
125
+ Executes a swap using the 0x Swap API. this is a sensitive function, show a preview and allow user to give final permission to execute function
126
 
127
  Args:
128
  -buy_token (str): The token to buy (e.g., 'DAI').