16
16
MONAD_BASE_GAS_PRICE = 50 # gwei - hardcoded for testnet
17
17
MONAD_CHAIN_ID = 10143
18
18
MONAD_SCANNER_URL = "testnet.monadexplorer.com"
19
- ZERO_EX_API_URL = "https://api.0x.org/swap/v2 "
19
+ ZERO_EX_API_URL = "https://api.0x.org/swap"
20
20
21
21
class MonadConnectionError (Exception ):
22
22
"""Base exception for Monad connection errors"""
@@ -83,7 +83,6 @@ def register_actions(self) -> None:
83
83
"get-balance" : Action (
84
84
name = "get-balance" ,
85
85
parameters = [
86
- ActionParameter ("address" , False , str , "Address to check balance for (optional)" ),
87
86
ActionParameter ("token_address" , False , str , "Token address (optional, native token if not provided)" )
88
87
],
89
88
description = "Get native or token balance"
@@ -308,20 +307,16 @@ def transfer(
308
307
logger .error (f"Transfer failed: { str (e )} " )
309
308
raise
310
309
311
- def _get_swap_quote (
312
- self ,
313
- token_in : str ,
314
- token_out : str ,
315
- amount : float ,
316
- sender : str
317
- ) -> Dict :
310
+ def _get_swap_quote (self , token_in : str , token_out : str , amount : float , sender : str ) -> Dict :
318
311
"""Get swap quote from 0x API using v2 endpoints"""
319
312
try :
320
313
load_dotenv ()
321
314
322
- # Convert amount to raw value with proper decimals
323
- if token_in .lower () == self .NATIVE_TOKEN .lower ():
315
+ # Use 0x API's native token identifier for ETH
316
+ if token_in == "0x0000000000000000000000000000000000000000" or token_in .lower () == self .NATIVE_TOKEN .lower ():
324
317
amount_raw = self ._web3 .to_wei (amount , 'ether' )
318
+ token_in = self .NATIVE_TOKEN
319
+ logger .debug (f"Using native token identifier: { token_in } " )
325
320
else :
326
321
token_contract = self ._web3 .eth .contract (
327
322
address = Web3 .to_checksum_address (token_in ),
@@ -341,21 +336,33 @@ def _get_swap_quote(
341
336
"buyToken" : token_out ,
342
337
"sellAmount" : str (amount_raw ),
343
338
"chainId" : str (self .chain_id ),
344
- "taker" : sender ,
345
- "gasPrice" : str (Web3 .to_wei (MONAD_BASE_GAS_PRICE , 'gwei' ))
339
+ "taker" : sender
346
340
}
347
341
348
342
url = f"{ ZERO_EX_API_URL } /permit2/quote"
349
-
343
+ logger . debug ( " \n HEADERS " )
350
344
logger .debug (headers )
345
+ logger .debug ("\n PARAMS " )
351
346
logger .debug (params )
347
+ logger .debug ("\n URL " )
352
348
logger .debug (url )
353
349
response = requests .get (
354
350
url ,
355
351
headers = headers ,
356
352
params = params
357
353
)
354
+ logger .debug (f"\n HEADER FROM OBJECT : { response .request .headers } " )
358
355
response .raise_for_status ()
356
+
357
+ # Log full response details
358
+ logger .info ("\n === 0x API Response ===" )
359
+ logger .info (f"Status Code: { response .status_code } " )
360
+ logger .info ("Headers:" )
361
+ for key , value in response .headers .items ():
362
+ logger .info (f"{ key } : { value } " )
363
+ logger .info ("\n Response Body:" )
364
+ logger .info (response .text )
365
+ logger .info ("=== End Response ===\n " )
359
366
360
367
data = response .json ()
361
368
return data
@@ -364,74 +371,24 @@ def _get_swap_quote(
364
371
logger .error (f"Failed to get swap quote: { str (e )} " )
365
372
raise
366
373
367
- def _handle_token_approval (
368
- self ,
369
- token_address : str ,
370
- spender_address : str ,
371
- amount : int
372
- ) -> Optional [str ]:
373
- """Handle token approval for spender, returns tx hash if approval needed"""
374
- try :
375
- account = self ._get_current_account ()
376
-
377
- token_contract = self ._web3 .eth .contract (
378
- address = Web3 .to_checksum_address (token_address ),
379
- abi = ERC20_ABI
380
- )
381
-
382
- # Check current allowance
383
- current_allowance = token_contract .functions .allowance (
384
- account .address ,
385
- spender_address
386
- ).call ()
387
-
388
- if current_allowance < amount :
389
- # Prepare approval transaction with fixed gas price
390
- approve_tx = token_contract .functions .approve (
391
- spender_address ,
392
- amount
393
- ).build_transaction ({
394
- 'from' : account .address ,
395
- 'nonce' : self ._web3 .eth .get_transaction_count (account .address ),
396
- 'gasPrice' : Web3 .to_wei (MONAD_BASE_GAS_PRICE , 'gwei' ),
397
- 'chainId' : self .chain_id
398
- })
399
-
400
- # Set fixed gas for approval on Monad
401
- approve_tx ['gas' ] = 100000 # Standard approval gas
402
-
403
- # Sign and send approval transaction
404
- signed_approve = account .sign_transaction (approve_tx )
405
- tx_hash = self ._web3 .eth .send_raw_transaction (signed_approve .rawTransaction )
406
-
407
- # Wait for approval to be mined
408
- receipt = self ._web3 .eth .wait_for_transaction_receipt (tx_hash )
409
- if receipt ['status' ] != 1 :
410
- raise ValueError ("Token approval failed" )
411
-
412
- return tx_hash .hex ()
413
-
414
- return None
415
-
416
- except Exception as e :
417
- logger .error (f"Token approval failed: { str (e )} " )
418
- raise
419
-
420
- def swap (
421
- self ,
422
- token_in : str ,
423
- token_out : str ,
424
- amount : float ,
425
- slippage : float = 0.5
426
- ) -> str :
374
+ def swap (self , token_in : str , token_out : str , amount : float , slippage : float = 0.5 ) -> str :
427
375
"""Execute token swap using 0x API with Monad-specific handling"""
428
376
try :
377
+ logger .debug (f"\n Starting swap with parameters:" )
378
+ logger .debug (f"token_in: { token_in } " )
379
+ logger .debug (f"token_out: { token_out } " )
380
+ logger .debug (f"amount: { amount } " )
381
+
429
382
account = self ._get_current_account ()
383
+ logger .debug (f"Account address: { account .address } " )
430
384
431
- # Validate balance including potential gas costs
432
- current_balance = self .get_balance (
433
- token_address = None if token_in .lower () == self .NATIVE_TOKEN .lower () else token_in
434
- )
385
+ # For native token swaps, use None as token_address for balance check
386
+ is_native = (token_in .lower () == self .NATIVE_TOKEN .lower () or
387
+ token_in == "0x0000000000000000000000000000000000000000" )
388
+
389
+ current_balance = self .get_balance (token_address = None if is_native else token_in )
390
+ logger .debug (f"Current balance: { current_balance } " )
391
+
435
392
if current_balance < amount :
436
393
raise ValueError (f"Insufficient balance. Required: { amount } , Available: { current_balance } " )
437
394
@@ -451,7 +408,7 @@ def swap(
451
408
raise ValueError ("Invalid transaction data in quote" )
452
409
453
410
# Handle token approval if needed for non-native tokens
454
- if token_in . lower () != self . NATIVE_TOKEN . lower () :
411
+ if not is_native :
455
412
spender_address = quote_data .get ("allowanceTarget" )
456
413
amount_raw = int (quote_data .get ("sellAmount" ))
457
414
@@ -467,7 +424,7 @@ def swap(
467
424
'from' : account .address ,
468
425
'to' : Web3 .to_checksum_address (transaction ["to" ]),
469
426
'data' : transaction ["data" ],
470
- 'value' : self ._web3 .to_wei (amount , 'ether' ) if token_in . lower () == self . NATIVE_TOKEN . lower () else 0 ,
427
+ 'value' : self ._web3 .to_wei (amount , 'ether' ) if is_native else 0 ,
471
428
'nonce' : self ._web3 .eth .get_transaction_count (account .address ),
472
429
'gasPrice' : Web3 .to_wei (MONAD_BASE_GAS_PRICE , 'gwei' ),
473
430
'chainId' : self .chain_id ,
@@ -491,6 +448,59 @@ def swap(
491
448
logger .error (f"Swap failed: { str (e )} " )
492
449
raise
493
450
451
+ def _handle_token_approval (
452
+ self ,
453
+ token_address : str ,
454
+ spender_address : str ,
455
+ amount : int
456
+ ) -> Optional [str ]:
457
+ """Handle token approval for spender, returns tx hash if approval needed"""
458
+ try :
459
+ account = self ._get_current_account ()
460
+
461
+ token_contract = self ._web3 .eth .contract (
462
+ address = Web3 .to_checksum_address (token_address ),
463
+ abi = ERC20_ABI
464
+ )
465
+
466
+ # Check current allowance
467
+ current_allowance = token_contract .functions .allowance (
468
+ account .address ,
469
+ spender_address
470
+ ).call ()
471
+
472
+ if current_allowance < amount :
473
+ # Prepare approval transaction with fixed gas price
474
+ approve_tx = token_contract .functions .approve (
475
+ spender_address ,
476
+ amount
477
+ ).build_transaction ({
478
+ 'from' : account .address ,
479
+ 'nonce' : self ._web3 .eth .get_transaction_count (account .address ),
480
+ 'gasPrice' : Web3 .to_wei (MONAD_BASE_GAS_PRICE , 'gwei' ),
481
+ 'chainId' : self .chain_id
482
+ })
483
+
484
+ # Set fixed gas for approval on Monad
485
+ approve_tx ['gas' ] = 100000 # Standard approval gas
486
+
487
+ # Sign and send approval transaction
488
+ signed_approve = account .sign_transaction (approve_tx )
489
+ tx_hash = self ._web3 .eth .send_raw_transaction (signed_approve .rawTransaction )
490
+
491
+ # Wait for approval to be mined
492
+ receipt = self ._web3 .eth .wait_for_transaction_receipt (tx_hash )
493
+ if receipt ['status' ] != 1 :
494
+ raise ValueError ("Token approval failed" )
495
+
496
+ return tx_hash .hex ()
497
+
498
+ return None
499
+
500
+ except Exception as e :
501
+ logger .error (f"Token approval failed: { str (e )} " )
502
+ raise
503
+
494
504
def perform_action (self , action_name : str , kwargs : Dict [str , Any ]) -> Any :
495
505
"""Execute a Monad action with validation"""
496
506
if action_name not in self .actions :
0 commit comments