Skip to content

Commit 0a3a7f4

Browse files
committed
trading sometimes not working for no reason
1 parent d9e7ed7 commit 0a3a7f4

File tree

1 file changed

+59
-41
lines changed

1 file changed

+59
-41
lines changed

src/connections/monad_connection.py

+59-41
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414

1515
# Constants specific to Monad testnet
1616
MONAD_BASE_GAS_PRICE = 50 # gwei - hardcoded for testnet
17+
MONAD_CHAIN_ID = 10143
18+
MONAD_SCANNER_URL = "testnet.monadexplorer.com"
19+
ZERO_EX_API_URL = "https://api.0x.org/swap/v2"
1720

1821
class MonadConnectionError(Exception):
1922
"""Base exception for Monad connection errors"""
@@ -30,8 +33,8 @@ def __init__(self, config: Dict[str, Any]):
3033
if not self.rpc_url:
3134
raise ValueError("RPC URL must be provided in config")
3235

33-
self.scanner_url = "testnet.monadexplorer.com"
34-
self.chain_id = config.get("chain_id", 10143)
36+
self.scanner_url = MONAD_SCANNER_URL
37+
self.chain_id = MONAD_CHAIN_ID
3538

3639
super().__init__(config)
3740
self._initialize_web3()
@@ -111,6 +114,13 @@ def register_actions(self) -> None:
111114
)
112115
}
113116

117+
def _get_current_account(self) -> 'LocalAccount':
118+
"""Get current account from private key"""
119+
private_key = os.getenv('MONAD_PRIVATE_KEY')
120+
if not private_key:
121+
raise MonadConnectionError("No wallet private key configured")
122+
return self._web3.eth.account.from_key(private_key)
123+
114124
def configure(self) -> bool:
115125
"""Sets up Monad wallet"""
116126
logger.info("\n⛓️ MONAD SETUP")
@@ -164,41 +174,38 @@ def is_configured(self, verbose: bool = False) -> bool:
164174
load_dotenv()
165175

166176
if not os.getenv('MONAD_PRIVATE_KEY'):
167-
logger.error("Missing MONAD_PRIVATE_KEY in .env")
177+
if verbose:
178+
logger.error("Missing MONAD_PRIVATE_KEY in .env")
168179
return False
169180

170181
if not self._web3.is_connected():
171-
logger.error("Not connected to Monad network")
182+
if verbose:
183+
logger.error("Not connected to Monad network")
172184
return False
173185

174186
# Test account access
175-
private_key = os.getenv('MONAD_PRIVATE_KEY')
176-
account = self._web3.eth.account.from_key(private_key)
187+
account = self._get_current_account()
177188
balance = self._web3.eth.get_balance(account.address)
178189

179190
return True
180191

181192
except Exception as e:
182-
logger.error(f"Configuration check failed: {e}")
193+
if verbose:
194+
logger.error(f"Configuration check failed: {e}")
183195
return False
184196

185197
def get_address(self) -> str:
186198
"""Get the wallet address"""
187199
try:
188-
private_key = os.getenv('MONAD_PRIVATE_KEY')
189-
account = self._web3.eth.account.from_key(private_key)
200+
account = self._get_current_account()
190201
return f"Your Monad address: {account.address}"
191202
except Exception as e:
192203
return f"Failed to get address: {str(e)}"
193204

194205
def get_balance(self, token_address: Optional[str] = None) -> float:
195206
"""Get native or token balance for the configured wallet"""
196207
try:
197-
private_key = os.getenv('MONAD_PRIVATE_KEY')
198-
if not private_key:
199-
return "No wallet private key configured in .env"
200-
201-
account = self._web3.eth.account.from_key(private_key)
208+
account = self._get_current_account()
202209

203210
if token_address is None or token_address.lower() == self.NATIVE_TOKEN.lower():
204211
raw_balance = self._web3.eth.get_balance(account.address)
@@ -224,8 +231,7 @@ def _prepare_transfer_tx(
224231
) -> Dict[str, Any]:
225232
"""Prepare transfer transaction with Monad-specific gas handling"""
226233
try:
227-
private_key = os.getenv('MONAD_PRIVATE_KEY')
228-
account = self._web3.eth.account.from_key(private_key)
234+
account = self._get_current_account()
229235

230236
# Get latest nonce
231237
nonce = self._web3.eth.get_transaction_count(account.address)
@@ -258,7 +264,7 @@ def _prepare_transfer_tx(
258264
'nonce': nonce,
259265
'to': Web3.to_checksum_address(to_address),
260266
'value': self._web3.to_wei(amount, 'ether'),
261-
'gas': 21000,
267+
'gas': 21000, # Standard ETH transfer gas
262268
'gasPrice': gas_price,
263269
'chainId': self.chain_id
264270
}
@@ -277,6 +283,8 @@ def transfer(
277283
) -> str:
278284
"""Transfer tokens with Monad-specific balance validation"""
279285
try:
286+
account = self._get_current_account()
287+
280288
# Check balance including gas cost since Monad charges on gas limit
281289
gas_cost = Web3.to_wei(MONAD_BASE_GAS_PRICE * 21000, 'gwei')
282290
gas_cost_eth = float(self._web3.from_wei(gas_cost, 'ether'))
@@ -290,9 +298,6 @@ def transfer(
290298

291299
# Prepare and send transaction
292300
tx = self._prepare_transfer_tx(to_address, amount, token_address)
293-
private_key = os.getenv('MONAD_PRIVATE_KEY')
294-
account = self._web3.eth.account.from_key(private_key)
295-
296301
signed = account.sign_transaction(tx)
297302
tx_hash = self._web3.eth.send_raw_transaction(signed.rawTransaction)
298303

@@ -310,9 +315,10 @@ def _get_swap_quote(
310315
amount: float,
311316
sender: str
312317
) -> Dict:
313-
"""Get swap quote from 0x API"""
318+
"""Get swap quote from 0x API using v2 endpoints"""
314319
try:
315320
load_dotenv()
321+
316322
# Convert amount to raw value with proper decimals
317323
if token_in.lower() == self.NATIVE_TOKEN.lower():
318324
amount_raw = self._web3.to_wei(amount, 'ether')
@@ -324,27 +330,30 @@ def _get_swap_quote(
324330
decimals = token_contract.functions.decimals().call()
325331
amount_raw = int(amount * (10 ** decimals))
326332

327-
# The URL should match exactly
328-
url = "https://api.0x.org/swap/permit2/quote"
329-
330-
# Headers should match exactly
333+
# Set up API request according to v2 spec
331334
headers = {
332335
"0x-api-key": os.getenv('ZEROEX_API_KEY'),
333336
"0x-version": "v2"
334337
}
335338

336-
# Parameters should match exactly
337339
params = {
338340
"sellToken": token_in,
339341
"buyToken": token_out,
340342
"sellAmount": str(amount_raw),
341-
"chainId": str(self.chain_id), # Convert to string to match exact format
342-
"taker": sender
343+
"chainId": str(self.chain_id),
344+
"taker": sender,
345+
"gasPrice": str(Web3.to_wei(MONAD_BASE_GAS_PRICE, 'gwei'))
343346
}
344347

345-
response = requests.get(url, headers=headers, params=params)
348+
response = requests.get(
349+
f"{ZERO_EX_API_URL}/permit2/quote",
350+
headers=headers,
351+
params=params
352+
)
346353
response.raise_for_status()
347-
return response.json()
354+
355+
data = response.json()
356+
return data
348357

349358
except Exception as e:
350359
logger.error(f"Failed to get swap quote: {str(e)}")
@@ -358,8 +367,7 @@ def _handle_token_approval(
358367
) -> Optional[str]:
359368
"""Handle token approval for spender, returns tx hash if approval needed"""
360369
try:
361-
private_key = os.getenv('MONAD_PRIVATE_KEY')
362-
account = self._web3.eth.account.from_key(private_key)
370+
account = self._get_current_account()
363371

364372
token_contract = self._web3.eth.contract(
365373
address=Web3.to_checksum_address(token_address),
@@ -411,10 +419,9 @@ def swap(
411419
amount: float,
412420
slippage: float = 0.5
413421
) -> str:
414-
"""Execute token swap using 0x API"""
422+
"""Execute token swap using 0x API with Monad-specific handling"""
415423
try:
416-
private_key = os.getenv('MONAD_PRIVATE_KEY')
417-
account = self._web3.eth.account.from_key(private_key)
424+
account = self._get_current_account()
418425

419426
# Validate balance including potential gas costs
420427
current_balance = self.get_balance(
@@ -423,7 +430,7 @@ def swap(
423430
if current_balance < amount:
424431
raise ValueError(f"Insufficient balance. Required: {amount}, Available: {current_balance}")
425432

426-
# Get swap quote
433+
# Get swap quote with v2 API
427434
quote_data = self._get_swap_quote(
428435
token_in,
429436
token_out,
@@ -433,12 +440,12 @@ def swap(
433440

434441
logger.debug(f"Quote data received: {quote_data}")
435442

436-
# Extract transaction data from the nested structure
443+
# Extract transaction data from quote
437444
transaction = quote_data.get("transaction")
438445
if not transaction or not transaction.get("to") or not transaction.get("data"):
439446
raise ValueError("Invalid transaction data in quote")
440447

441-
# Handle token approval if needed
448+
# Handle token approval if needed for non-native tokens
442449
if token_in.lower() != self.NATIVE_TOKEN.lower():
443450
spender_address = quote_data.get("allowanceTarget")
444451
amount_raw = int(quote_data.get("sellAmount"))
@@ -450,7 +457,7 @@ def swap(
450457
# Wait for approval confirmation
451458
self._web3.eth.wait_for_transaction_receipt(approval_hash)
452459

453-
# Prepare swap transaction using the nested transaction data
460+
# Prepare swap transaction using quote data
454461
tx = {
455462
'from': account.address,
456463
'to': Web3.to_checksum_address(transaction["to"]),
@@ -459,9 +466,15 @@ def swap(
459466
'nonce': self._web3.eth.get_transaction_count(account.address),
460467
'gasPrice': Web3.to_wei(MONAD_BASE_GAS_PRICE, 'gwei'),
461468
'chainId': self.chain_id,
462-
'gas': int(transaction.get("gas", 500000)) # Use gas from quote or fallback to 500000
463469
}
464470

471+
# Estimate gas or use quote's gas estimate
472+
try:
473+
tx['gas'] = int(transaction.get("gas", 0)) or self._web3.eth.estimate_gas(tx)
474+
except Exception as e:
475+
logger.warning(f"Gas estimation failed: {e}, using default gas limit")
476+
tx['gas'] = 500000 # Default gas limit for swaps
477+
465478
# Sign and send transaction
466479
signed_tx = account.sign_transaction(tx)
467480
tx_hash = self._web3.eth.send_raw_transaction(signed_tx.rawTransaction)
@@ -490,4 +503,9 @@ def perform_action(self, action_name: str, kwargs: Dict[str, Any]) -> Any:
490503

491504
method_name = action_name.replace('-', '_')
492505
method = getattr(self, method_name)
493-
return method(**kwargs)
506+
507+
try:
508+
return method(**kwargs)
509+
except Exception as e:
510+
logger.error(f"Action {action_name} failed: {str(e)}")
511+
raise

0 commit comments

Comments
 (0)