1111from ccxt .base .errors import AuthenticationError
1212from ccxt .base .errors import ArgumentsRequired
1313from ccxt .base .errors import BadRequest
14+ from ccxt .base .errors import InvalidOrder
1415from ccxt .base .errors import OrderNotFound
1516from ccxt .base .decimal_to_precision import TICK_SIZE
1617
@@ -154,6 +155,8 @@ def describe(self) -> Any:
154155 },
155156 },
156157 'options' : {
158+ 'defaultAPIKey' : '' ,
159+ 'forceDefaultAPIKey' : False ,
157160 'statusMapping' : {
158161 'new' : 'open' ,
159162 'waiting' : 'open' ,
@@ -169,6 +172,31 @@ def describe(self) -> Any:
169172 },
170173 })
171174
175+ def resolve_api_key (self ) -> Str :
176+ if self .safe_bool (self .options , 'forceDefaultAPIKey' , False ):
177+ return self .safe_string (self .options , 'defaultAPIKey' )
178+ if self .apiKey :
179+ return self .apiKey
180+ return self .safe_string (self .options , 'defaultAPIKey' )
181+
182+ def check_required_credentials (self , error = True ):
183+ keys = list (self .requiredCredentials .keys ())
184+ for credentialIndex in range (0 , len (keys )):
185+ key = keys [credentialIndex ]
186+ if self .requiredCredentials [key ]:
187+ if key == 'apiKey' :
188+ if not self .resolve_api_key ():
189+ if error :
190+ raise AuthenticationError (self .id + ' requires "' + key + '" credential' )
191+ else :
192+ return False
193+ elif not getattr (self , key ):
194+ if error :
195+ raise AuthenticationError (self .id + ' requires "' + key + '" credential' )
196+ else :
197+ return False
198+ return True
199+
172200 async def fetch_currencies (self , params = {}) -> Currencies :
173201 """
174202 fetches all available currencies on ChangeNOW
@@ -330,7 +358,7 @@ async def fetch_ticker(self, symbol: str, params={}) -> Ticker:
330358 response = None
331359 if fixedRate :
332360 # v1 fixed-rate estimate requires api_key query parameter
333- request ['api_key' ] = self .apiKey
361+ request ['api_key' ] = self .resolve_api_key ()
334362 response = await self .publicGetExchangeAmountFixedRateAmountFromTo (self .extend (request , params ))
335363 else :
336364 response = await self .publicGetExchangeAmountAmountFromTo (self .extend (request , params ))
@@ -389,9 +417,9 @@ async def create_order(self, symbol: str, type: OrderType, side: OrderSide, amou
389417 :param float amount: amount of the base currency to send
390418 :param float [price]: not used for ChangeNOW standard flow
391419 :param dict [params]: extra parameters specific to the exchange API endpoint
392- :param str params['address ']: destination address for the quote currency(required)
420+ :param str params['address_to ']: destination address for the quote currency(required)
393421 :param str [params.extraId]: extra id or memo for the destination address(e.g. for XRP, XLM)
394- :param str [params.refundAddress ]: refund address for the base currency
422+ :param str [params.refund_address ]: refund address for the base currency
395423 :param str [params.refundExtraId]: extra id or memo for the refund address
396424 :param str [params.rateId]: rate id from fixed-rate estimate(required for fixed-rate)
397425 :param str [params.contactEmail]: contact email for notifications
@@ -402,20 +430,20 @@ async def create_order(self, symbol: str, type: OrderType, side: OrderSide, amou
402430 market = self .market (symbol )
403431 baseId = self .safe_string (market , 'baseId' )
404432 quoteId = self .safe_string (market , 'quoteId' )
405- address = self .safe_string (params , 'address ' )
406- if address is None :
407- raise ArgumentsRequired (self .id + ' createOrder() requires params.address – the destination address for the received currency' )
433+ addressTo = self .safe_string (params , 'address_to ' )
434+ if addressTo is None :
435+ raise ArgumentsRequired (self .id + ' createOrder() requires params.address_to – the destination address for the received currency' )
408436 request : dict = {
409- 'apiKey' : self .apiKey ,
437+ 'apiKey' : self .resolve_api_key () ,
410438 'from' : baseId ,
411439 'to' : quoteId ,
412440 'amount' : self .number_to_string (amount ),
413- 'address' : address ,
441+ 'address' : addressTo ,
414442 }
415443 extraId = self .safe_string (params , 'extraId' )
416444 if extraId is not None :
417445 request ['extraId' ] = extraId
418- refundAddress = self .safe_string (params , 'refundAddress ' )
446+ refundAddress = self .safe_string (params , 'refund_address ' )
419447 if refundAddress is not None :
420448 request ['refundAddress' ] = refundAddress
421449 refundExtraId = self .safe_string (params , 'refundExtraId' )
@@ -427,7 +455,7 @@ async def create_order(self, symbol: str, type: OrderType, side: OrderSide, amou
427455 contactEmail = self .safe_string (params , 'contactEmail' )
428456 if contactEmail is not None :
429457 request ['contactEmail' ] = contactEmail
430- params = self .omit (params , ['address ' , 'extraId' , 'refundAddress ' , 'refundExtraId' , 'rateId' , 'contactEmail' ])
458+ params = self .omit (params , ['address_to ' , 'extraId' , 'refund_address ' , 'refundExtraId' , 'rateId' , 'contactEmail' ])
431459 response = await self .privatePostTransactionsApiKey (self .extend (request , params ))
432460 #
433461 # {
@@ -456,7 +484,7 @@ async def fetch_order(self, id: str, symbol: Str = None, params={}) -> Order:
456484 self .check_required_credentials ()
457485 request : dict = {
458486 'id' : id ,
459- 'apiKey' : self .apiKey ,
487+ 'apiKey' : self .resolve_api_key () ,
460488 }
461489 response = await self .privateGetTransactionsIdApiKey (self .extend (request , params ))
462490 #
@@ -577,6 +605,7 @@ def handle_errors(self, httpCode: int, reason: str, url: str, method: str, heade
577605 # {"error": "pair_is_inactive", "message": "This pair is currently unavailable"}
578606 # {"error": "not_valid_params", "message": "Invalid request parameters"}
579607 # {"error": "deposit_too_small", "message": "Deposit amount is less than minimum"}
608+ # {"error": "out_of_range", "message": "Amount is less then minimal: 0.0223925 XMR"}
580609 # {"message": "Unauthorized"}
581610 #
582611 error = self .safe_string (response , 'error' )
@@ -588,6 +617,8 @@ def handle_errors(self, httpCode: int, reason: str, url: str, method: str, heade
588617 raise AuthenticationError (self .id + ' ' + body )
589618 if error == 'not_found' :
590619 raise OrderNotFound (self .id + ' ' + body )
620+ if error == 'deposit_too_small' or error == 'out_of_range' :
621+ raise InvalidOrder (self .id + ' ' + body )
591622 raise ExchangeError (self .id + ' ' + body )
592623 if message is not None and httpCode >= 400 :
593624 if httpCode == 401 or httpCode == 403 :
0 commit comments