mezoAgent 0.2.4__tar.gz → 0.3.1__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mezoAgent
3
- Version: 0.2.4
3
+ Version: 0.3.1
4
4
  Summary: A LangChain based tool kit for AI Agents to send BTC and mUSD transactions on Mezo Matsnet.
5
5
  Author: Dreadwulf, Duck, Digi
6
6
  Requires-Python: >=3.7
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mezoAgent
3
- Version: 0.2.4
3
+ Version: 0.3.1
4
4
  Summary: A LangChain based tool kit for AI Agents to send BTC and mUSD transactions on Mezo Matsnet.
5
5
  Author: Dreadwulf, Duck, Digi
6
6
  Requires-Python: >=3.7
@@ -8,4 +8,5 @@ mezoAgent.egg-info/top_level.txt
8
8
  mezo_agent/__init__.py
9
9
  mezo_agent/config.py
10
10
  mezo_agent/parsing.py
11
+ mezo_agent/swap_musd_btc.py
11
12
  mezo_agent/transaction.py
@@ -0,0 +1,2 @@
1
+ from .transaction import mezo_agent_transaction_btc, mezo_agent_musd_transaction
2
+ from .swap_musd_btc import mezo_agent_swap_musd_btc
@@ -0,0 +1,50 @@
1
+ import os
2
+ import json
3
+ from dotenv import load_dotenv
4
+ from web3 import Web3
5
+
6
+ # Load environment variables
7
+ load_dotenv()
8
+
9
+ USER_PROJECT_DIR = os.getcwd()
10
+ USER_ENV_PATH = os.path.join(USER_PROJECT_DIR, ".env")
11
+
12
+ if os.path.exists(USER_ENV_PATH):
13
+ load_dotenv(USER_ENV_PATH)
14
+ else:
15
+ print("⚠️ Warning: No `.env` file found in your project directory! Transactions requiring signing may fail.")
16
+
17
+ PRIVATE_KEY = os.getenv("PRIVATE_KEY")
18
+ OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
19
+
20
+ if not PRIVATE_KEY:
21
+ print("⚠️ Warning: PRIVATE_KEY not set. Please create a `.env` file in your project with your keys.")
22
+ PRIVATE_KEY = None # Allow package to be installed but prevent transactions
23
+
24
+ # Mezo Testnet RPC and Web3 initialization
25
+ RPC_URL = "https://rpc.test.mezo.org"
26
+ web3_instance = Web3(Web3.HTTPProvider(RPC_URL))
27
+
28
+ # Create Account Object
29
+ account = web3_instance.eth.account.from_key(PRIVATE_KEY)
30
+ sender_address = account.address
31
+
32
+ # mUSD Contract Setup using approve/allowance ABI
33
+ MUSD_ADDRESS = "0x637e22A1EBbca50EA2d34027c238317fD10003eB"
34
+ ERC20_ABI = json.loads(
35
+ '[{"constant": false, "inputs": [{"name": "spender", "type": "address"}, {"name": "amount", "type": "uint256"}],'
36
+ '"name": "approve", "outputs": [{"name": "", "type": "bool"}], "stateMutability": "nonpayable", "type": "function"},'
37
+ '{"constant": true, "inputs": [{"name": "owner", "type": "address"}, {"name": "spender", "type": "address"}],'
38
+ '"name": "allowance", "outputs": [{"name": "remaining", "type": "uint256"}], "stateMutability": "view", "type": "function"}]'
39
+ )
40
+ musd_contract = web3_instance.eth.contract(address=MUSD_ADDRESS, abi=ERC20_ABI)
41
+
42
+ # Wrapped BTC and Swap Router setup
43
+ WRAPPED_BTC_ADDRESS = "0xA460F83cdd9584E4bD6a9838abb0baC58EAde999"
44
+ ROUTER_ADDRESS = "0xC2E61936a542D78b9c3AA024fA141c4C632DF6c1"
45
+
46
+ # Load router ABI from a JSON file packaged with mezoAgent
47
+ router_abi_path = os.path.join(os.path.dirname(__file__), "new_router_abi.json")
48
+ with open(router_abi_path, "r") as f:
49
+ router_abi = json.load(f)
50
+ router_contract = web3_instance.eth.contract(address=ROUTER_ADDRESS, abi=router_abi)
@@ -33,4 +33,33 @@ def extract_transaction_details(prompt: str):
33
33
  try:
34
34
  return output_parser.parse(response.content)
35
35
  except Exception as e:
36
- return f"❌ Failed to extract transaction details: {str(e)}"
36
+ return f"❌ Failed to extract transaction details: {str(e)}"
37
+
38
+
39
+ swap_response_schemas = [
40
+ ResponseSchema(name="amount", description="The amount of mUSD to swap."),
41
+ ResponseSchema(name="from_currency", description="The token to swap from (should always be 'mUSD')."),
42
+ ResponseSchema(name="to_currency", description="The token to receive (should always be 'BTC')."),
43
+ ResponseSchema(name="router_address", description="The Dumpy Swap router address for executing the swap."),
44
+ ]
45
+ swap_output_parser = StructuredOutputParser.from_response_schemas(swap_response_schemas)
46
+
47
+ swap_prompt_template = PromptTemplate(
48
+ template=(
49
+ "Extract swap transaction details from this request:\n{input}\n\n"
50
+ "- The token to swap from should always be 'mUSD'.\n"
51
+ "- The token to receive should always be 'BTC'.\n"
52
+ "- The router address should always be '0xC2E61936a542D78b9c3AA024fA141c4C632DF6c1'.\n\n"
53
+ "{format_instructions}"
54
+ ),
55
+ input_variables=["input"],
56
+ partial_variables={"format_instructions": swap_output_parser.get_format_instructions()},
57
+ )
58
+
59
+ def extract_swap_details(prompt: str):
60
+ formatted_prompt = swap_prompt_template.format(input=prompt)
61
+ response = llm.invoke(formatted_prompt)
62
+ try:
63
+ return swap_output_parser.parse(response.content)
64
+ except Exception as e:
65
+ return f"Failed to extract swap details: {str(e)}"
@@ -0,0 +1,112 @@
1
+ import time
2
+ from langchain.tools import tool
3
+ from .config import (
4
+ web3_instance,
5
+ sender_address,
6
+ account,
7
+ PRIVATE_KEY,
8
+ musd_contract,
9
+ ROUTER_ADDRESS,
10
+ router_contract,
11
+ MUSD_ADDRESS,
12
+ WRAPPED_BTC_ADDRESS
13
+ )
14
+ from .parsing import extract_swap_details
15
+
16
+ def approve_if_needed(token_contract, amount_wei):
17
+ """
18
+ Checks if the router has enough allowance to spend tokens.
19
+ If not, sends an approval transaction.
20
+ """
21
+ current_allowance = token_contract.functions.allowance(sender_address, ROUTER_ADDRESS).call()
22
+ if current_allowance < amount_wei:
23
+ print(f"Current allowance ({current_allowance}) is less than required ({amount_wei}). Approving...")
24
+ nonce = web3_instance.eth.get_transaction_count(sender_address)
25
+ gas_price = web3_instance.eth.gas_price
26
+ approve_tx = token_contract.functions.approve(ROUTER_ADDRESS, amount_wei).build_transaction({
27
+ "from": sender_address,
28
+ "nonce": nonce,
29
+ "gas": 50000,
30
+ "gasPrice": gas_price,
31
+ })
32
+ signed_tx = web3_instance.eth.account.sign_transaction(approve_tx, PRIVATE_KEY)
33
+ tx_hash = web3_instance.eth.send_raw_transaction(signed_tx.raw_transaction)
34
+ receipt = web3_instance.eth.wait_for_transaction_receipt(tx_hash)
35
+ if receipt.status != 1:
36
+ raise Exception("Approval transaction failed.")
37
+ print(f"Approval successful. TX Hash: {tx_hash.hex()}")
38
+ else:
39
+ print("Sufficient allowance already set.")
40
+
41
+ @tool
42
+ def mezo_agent_swap_musd_btc(prompt: str) -> str:
43
+ """
44
+ Swaps mUSD for Wrapped BTC using the Dumpy Swap router.
45
+
46
+ This tool extracts swap details from the prompt, approves the router if needed,
47
+ and executes the swap transaction.
48
+
49
+ The prompt should specify the mUSD amount to swap.
50
+ """
51
+ swap_details = extract_swap_details(prompt)
52
+ if isinstance(swap_details, str):
53
+ return swap_details
54
+
55
+ try:
56
+ amount_musd = float(swap_details["amount"])
57
+ except KeyError as e:
58
+ return f"❌ Missing key in swap details: {str(e)}"
59
+
60
+ # For demonstration, we use a dummy minimum Wrapped BTC amount.
61
+ min_wrapped_btc = 0.000000000000001 # in BTC
62
+
63
+ # Convert amounts to Wei (assuming 18 decimals)
64
+ amount_musd_wei = int(amount_musd * 10**18)
65
+ min_wrapped_btc_wei = int(min_wrapped_btc * 10**18)
66
+ deadline = int(time.time()) + 600 # 10 minutes from now
67
+
68
+ # Approve the router to spend mUSD if needed
69
+ try:
70
+ approve_if_needed(musd_contract, amount_musd_wei)
71
+ except Exception as e:
72
+ return f"❌ Approval failed: {str(e)}"
73
+
74
+ # Define the swap path: mUSD -> Wrapped BTC
75
+ path = [MUSD_ADDRESS, WRAPPED_BTC_ADDRESS]
76
+
77
+ nonce = web3_instance.eth.get_transaction_count(sender_address)
78
+ gas_price = web3_instance.eth.gas_price
79
+
80
+ try:
81
+ swap_tx = router_contract.functions.swapExactTokensForTokens(
82
+ amount_musd_wei, # mUSD amount
83
+ min_wrapped_btc_wei, # Minimum Wrapped BTC to receive
84
+ path, # Swap path
85
+ sender_address, # Recipient address
86
+ deadline # Transaction deadline
87
+ ).build_transaction({
88
+ "from": sender_address,
89
+ "nonce": nonce,
90
+ "gasPrice": gas_price,
91
+ })
92
+ except Exception as e:
93
+ return f"❌ Failed to build swap transaction: {str(e)}"
94
+
95
+ # Estimate gas and add a buffer
96
+ try:
97
+ estimated_gas = web3_instance.eth.estimate_gas(swap_tx)
98
+ swap_tx["gas"] = estimated_gas + 10000
99
+ except Exception as e:
100
+ swap_tx["gas"] = 250000 # Fallback gas limit
101
+
102
+ try:
103
+ signed_swap_tx = web3_instance.eth.account.sign_transaction(swap_tx, PRIVATE_KEY)
104
+ tx_hash = web3_instance.eth.send_raw_transaction(signed_swap_tx.raw_transaction)
105
+ except Exception as e:
106
+ return f"❌ Swap transaction failed: {str(e)}"
107
+
108
+ receipt = web3_instance.eth.wait_for_transaction_receipt(tx_hash)
109
+ if receipt.status != 1:
110
+ return f"❌ Swap transaction failed. Receipt: {receipt}"
111
+
112
+ return f"✅ Swap Successful! TX Hash: {tx_hash.hex()}"
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name="mezoAgent",
5
- version="0.2.4",
5
+ version="0.3.1",
6
6
  packages=find_packages(),
7
7
  install_requires=[
8
8
  "web3",
@@ -1 +0,0 @@
1
- from .transaction import mezo_agent_transaction_btc, mezo_agent_musd_transaction
@@ -1,46 +0,0 @@
1
- import os
2
- import json
3
- from dotenv import load_dotenv
4
- from web3 import Web3
5
- from web3.exceptions import Web3Exception
6
-
7
- # ✅ Load environment variables
8
- USER_PROJECT_DIR = os.getcwd()
9
- USER_ENV_PATH = os.path.join(USER_PROJECT_DIR, ".env")
10
-
11
- if os.path.exists(USER_ENV_PATH):
12
- load_dotenv(USER_ENV_PATH)
13
- else:
14
- print("⚠️ Warning: No `.env` file found in your project directory! Transactions requiring signing may fail.")
15
-
16
- # ✅ Load keys from the environment
17
- PRIVATE_KEY = os.getenv("PRIVATE_KEY")
18
- OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
19
-
20
- if not PRIVATE_KEY:
21
- print("⚠️ Warning: PRIVATE_KEY not set. Please create a `.env` file in your project with your keys.")
22
- PRIVATE_KEY = None # Allow package to be installed but prevent transactions
23
-
24
- # ✅ Define RPC URL and initialize Web3
25
- RPC_URL = "https://rpc.test.mezo.org"
26
- web3_instance = Web3(Web3.HTTPProvider(RPC_URL))
27
-
28
- if not web3_instance.is_connected():
29
- raise ConnectionError("❌ Failed to connect to Mezo Matsnet RPC.")
30
-
31
- # ✅ Initialize account and sender address
32
- if PRIVATE_KEY:
33
- account = web3_instance.eth.account.from_key(PRIVATE_KEY)
34
- sender_address = account.address
35
- else:
36
- account = None
37
- sender_address = None
38
-
39
- # ✅ Set up contract details (for mUSD)
40
- MUSD_ADDRESS = "0x637e22A1EBbca50EA2d34027c238317fD10003eB"
41
- ERC20_ABI = json.loads(
42
- '[{"constant": false, "inputs": [{"name": "recipient", "type": "address"},'
43
- '{"name": "amount", "type": "uint256"}],"name": "transfer","outputs": [{"name": "", "type": "bool"}],'
44
- '"stateMutability": "nonpayable","type":"function"}]'
45
- )
46
- musd_contract = web3_instance.eth.contract(address=MUSD_ADDRESS, abi=ERC20_ABI)
File without changes
File without changes