swapzone 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- swapzone/__init__.py +23 -0
- swapzone/client.py +341 -0
- swapzone/errors.py +21 -0
- swapzone/types.py +71 -0
- swapzone-0.1.0.dist-info/METADATA +198 -0
- swapzone-0.1.0.dist-info/RECORD +8 -0
- swapzone-0.1.0.dist-info/WHEEL +4 -0
- swapzone-0.1.0.dist-info/licenses/LICENSE +15 -0
swapzone/__init__.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""Swapzone cryptocurrency exchange and DEX aggregator client."""
|
|
2
|
+
|
|
3
|
+
from .client import Swapzone
|
|
4
|
+
from .errors import SwapzoneError
|
|
5
|
+
from .types import (
|
|
6
|
+
DEXBlockchain,
|
|
7
|
+
RateOffer,
|
|
8
|
+
SupportedCurrency,
|
|
9
|
+
TransactionResult,
|
|
10
|
+
TransactionStatus,
|
|
11
|
+
ValidateAddressResult,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"DEXBlockchain",
|
|
16
|
+
"RateOffer",
|
|
17
|
+
"SupportedCurrency",
|
|
18
|
+
"Swapzone",
|
|
19
|
+
"SwapzoneError",
|
|
20
|
+
"TransactionResult",
|
|
21
|
+
"TransactionStatus",
|
|
22
|
+
"ValidateAddressResult",
|
|
23
|
+
]
|
swapzone/client.py
ADDED
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
import httpx
|
|
5
|
+
|
|
6
|
+
from .errors import SwapzoneError
|
|
7
|
+
from .types import (
|
|
8
|
+
DEXBlockchain,
|
|
9
|
+
RateOffer,
|
|
10
|
+
SupportedCurrency,
|
|
11
|
+
TransactionResult,
|
|
12
|
+
TransactionStatus,
|
|
13
|
+
ValidateAddressResult,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class Swapzone:
|
|
18
|
+
"""Client for the Swapzone cryptocurrency exchange and DEX aggregator API.
|
|
19
|
+
|
|
20
|
+
Attributes:
|
|
21
|
+
api_key: Swapzone API key.
|
|
22
|
+
base_url: Base URL of the API.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def __init__(
|
|
26
|
+
self,
|
|
27
|
+
api_key: str | None = None,
|
|
28
|
+
base_url: str | None = None,
|
|
29
|
+
timeout: float = 30.0,
|
|
30
|
+
user_agent: str | None = None,
|
|
31
|
+
) -> None:
|
|
32
|
+
"""Initializes the Swapzone client.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
api_key: Swapzone API key. Defaults to SWAPZONE_API_KEY environment variable.
|
|
36
|
+
base_url: Base URL for the Swapzone API. Defaults to https://api.swapzone.io.
|
|
37
|
+
timeout: Timeout in seconds for HTTP requests. Defaults to 30.0.
|
|
38
|
+
user_agent: Custom User-Agent header string.
|
|
39
|
+
|
|
40
|
+
Examples:
|
|
41
|
+
>>> from swapzone import Swapzone
|
|
42
|
+
>>> client = Swapzone(api_key="your-api-key")
|
|
43
|
+
>>> currencies = client.get_currencies()
|
|
44
|
+
>>> print(len(currencies))
|
|
45
|
+
"""
|
|
46
|
+
self.api_key = api_key or os.environ.get("SWAPZONE_API_KEY")
|
|
47
|
+
self.base_url = (base_url or "https://api.swapzone.io").rstrip("/")
|
|
48
|
+
ua = user_agent or "swapzone-client/0.1.0"
|
|
49
|
+
|
|
50
|
+
headers: dict[str, str] = {
|
|
51
|
+
"User-Agent": ua,
|
|
52
|
+
"Accept": "application/json",
|
|
53
|
+
"Content-Type": "application/json",
|
|
54
|
+
}
|
|
55
|
+
if self.api_key:
|
|
56
|
+
headers["x-api-key"] = self.api_key
|
|
57
|
+
|
|
58
|
+
self._client = httpx.Client(
|
|
59
|
+
base_url=self.base_url,
|
|
60
|
+
timeout=timeout,
|
|
61
|
+
headers=headers,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
def close(self) -> None:
|
|
65
|
+
"""Closes the underlying HTTP client session."""
|
|
66
|
+
self._client.close()
|
|
67
|
+
|
|
68
|
+
def __enter__(self) -> "Swapzone":
|
|
69
|
+
return self
|
|
70
|
+
|
|
71
|
+
def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
|
|
72
|
+
self.close()
|
|
73
|
+
|
|
74
|
+
def _request(
|
|
75
|
+
self,
|
|
76
|
+
method: str,
|
|
77
|
+
path: str,
|
|
78
|
+
params: dict[str, Any] | None = None,
|
|
79
|
+
json_data: dict[str, Any] | None = None,
|
|
80
|
+
) -> Any:
|
|
81
|
+
try:
|
|
82
|
+
response = self._client.request(
|
|
83
|
+
method,
|
|
84
|
+
path,
|
|
85
|
+
params=params,
|
|
86
|
+
json=json_data,
|
|
87
|
+
)
|
|
88
|
+
response.raise_for_status()
|
|
89
|
+
data = response.json()
|
|
90
|
+
|
|
91
|
+
if isinstance(data, dict) and data.get("error") is True:
|
|
92
|
+
msg = data.get("message") or "Swapzone API error"
|
|
93
|
+
raise SwapzoneError(
|
|
94
|
+
message=str(msg),
|
|
95
|
+
status_code=response.status_code,
|
|
96
|
+
response_body=data,
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
return data
|
|
100
|
+
except httpx.HTTPStatusError as e:
|
|
101
|
+
try:
|
|
102
|
+
data_err = e.response.json()
|
|
103
|
+
except Exception:
|
|
104
|
+
data_err = None
|
|
105
|
+
msg = "Swapzone API error"
|
|
106
|
+
if isinstance(data_err, dict) and data_err.get("message"):
|
|
107
|
+
msg = str(data_err["message"])
|
|
108
|
+
raise SwapzoneError(
|
|
109
|
+
message=msg,
|
|
110
|
+
status_code=e.response.status_code,
|
|
111
|
+
response_body=data_err,
|
|
112
|
+
) from e
|
|
113
|
+
except httpx.RequestError as e:
|
|
114
|
+
raise SwapzoneError(f"HTTP request failed: {e}") from e
|
|
115
|
+
|
|
116
|
+
def get_currencies(self) -> list[SupportedCurrency]:
|
|
117
|
+
"""Retrieves all supported cryptocurrencies and tokens.
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
List of supported currencies with tickers and networks.
|
|
121
|
+
|
|
122
|
+
Examples:
|
|
123
|
+
>>> currencies = client.get_currencies()
|
|
124
|
+
>>> print(currencies[0]["ticker"])
|
|
125
|
+
"""
|
|
126
|
+
return self._request("GET", "/v1/exchange/currencies")
|
|
127
|
+
|
|
128
|
+
def get_rate(
|
|
129
|
+
self,
|
|
130
|
+
from_currency: str,
|
|
131
|
+
to_currency: str,
|
|
132
|
+
amount: float | str,
|
|
133
|
+
rate_type: str = "all",
|
|
134
|
+
choose_rate: str = "best",
|
|
135
|
+
available_in_usa: bool | None = None,
|
|
136
|
+
no_refund_address: bool | None = None,
|
|
137
|
+
from_network: str | None = None,
|
|
138
|
+
to_network: str | None = None,
|
|
139
|
+
amount_to: float | str | None = None,
|
|
140
|
+
timeout: float | str | None = None,
|
|
141
|
+
) -> RateOffer:
|
|
142
|
+
"""Retrieves rate estimates for an exchange pair.
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
from_currency: Deposit asset ticker (e.g. 'btc').
|
|
146
|
+
to_currency: Receiving asset ticker (e.g. 'eth').
|
|
147
|
+
amount: Deposit amount.
|
|
148
|
+
rate_type: Rate type ('all', 'fixed', or 'floating').
|
|
149
|
+
choose_rate: Strategy ('best', 'all', or 'recommended').
|
|
150
|
+
available_in_usa: Restrict to partners supporting US clients.
|
|
151
|
+
no_refund_address: Restrict to exchangers with optional refund address.
|
|
152
|
+
from_network: Network for deposit asset.
|
|
153
|
+
to_network: Network for receiving asset.
|
|
154
|
+
amount_to: Target receiving amount (for reverse rate).
|
|
155
|
+
timeout: Maximum response timeout in ms.
|
|
156
|
+
|
|
157
|
+
Returns:
|
|
158
|
+
Rate offer dictionary with amounts and quota ID.
|
|
159
|
+
|
|
160
|
+
Examples:
|
|
161
|
+
>>> rate = client.get_rate("btc", "eth", amount="0.1")
|
|
162
|
+
>>> print(f"ETH: {rate['amountTo']} via {rate['adapter']}")
|
|
163
|
+
"""
|
|
164
|
+
params: dict[str, Any] = {
|
|
165
|
+
"from": from_currency,
|
|
166
|
+
"to": to_currency,
|
|
167
|
+
"amount": str(amount),
|
|
168
|
+
"rateType": rate_type,
|
|
169
|
+
"chooseRate": choose_rate,
|
|
170
|
+
}
|
|
171
|
+
if available_in_usa is not None:
|
|
172
|
+
params["availableInUSA"] = available_in_usa
|
|
173
|
+
if no_refund_address is not None:
|
|
174
|
+
params["noRefundAddress"] = no_refund_address
|
|
175
|
+
if from_network is not None:
|
|
176
|
+
params["fromNetwork"] = from_network
|
|
177
|
+
if to_network is not None:
|
|
178
|
+
params["toNetwork"] = to_network
|
|
179
|
+
if amount_to is not None:
|
|
180
|
+
params["amountTo"] = str(amount_to)
|
|
181
|
+
if timeout is not None:
|
|
182
|
+
params["timeout"] = str(timeout)
|
|
183
|
+
|
|
184
|
+
return self._request("GET", "/v1/exchange/get-rate", params=params)
|
|
185
|
+
|
|
186
|
+
def create_transaction(
|
|
187
|
+
self,
|
|
188
|
+
from_currency: str,
|
|
189
|
+
to_currency: str,
|
|
190
|
+
amount_deposit: float | str,
|
|
191
|
+
address_receive: str,
|
|
192
|
+
extra_id_receive: str | None = None,
|
|
193
|
+
refund_address: str | None = None,
|
|
194
|
+
refund_extra_id: str | None = None,
|
|
195
|
+
quota_id: str | None = None,
|
|
196
|
+
no_quota_id: bool | None = None,
|
|
197
|
+
from_network: str | None = None,
|
|
198
|
+
to_network: str | None = None,
|
|
199
|
+
offer_reference_id: str | None = None,
|
|
200
|
+
) -> TransactionResult:
|
|
201
|
+
"""Creates an exchange transaction order.
|
|
202
|
+
|
|
203
|
+
Args:
|
|
204
|
+
from_currency: Deposit asset ticker.
|
|
205
|
+
to_currency: Receiving asset ticker.
|
|
206
|
+
amount_deposit: Amount to deposit.
|
|
207
|
+
address_receive: Destination recipient wallet address.
|
|
208
|
+
extra_id_receive: Optional memo / extra ID for recipient.
|
|
209
|
+
refund_address: Optional address to return funds on failure.
|
|
210
|
+
refund_extra_id: Optional memo for refund address.
|
|
211
|
+
quota_id: Quota ID obtained from get_rate.
|
|
212
|
+
no_quota_id: Bypass quota ID requirement.
|
|
213
|
+
from_network: Deposit blockchain network.
|
|
214
|
+
to_network: Receiving blockchain network.
|
|
215
|
+
offer_reference_id: Reference ID for fixed-rate quotes.
|
|
216
|
+
|
|
217
|
+
Returns:
|
|
218
|
+
Order details containing deposit address and transaction ID.
|
|
219
|
+
|
|
220
|
+
Examples:
|
|
221
|
+
>>> order = client.create_transaction(
|
|
222
|
+
... from_currency="btc",
|
|
223
|
+
... to_currency="eth",
|
|
224
|
+
... amount_deposit="0.1",
|
|
225
|
+
... address_receive="0xReceiverAddress...",
|
|
226
|
+
... quota_id=rate["quotaId"],
|
|
227
|
+
... )
|
|
228
|
+
>>> print(f"Deposit address: {order['addressDeposit']}")
|
|
229
|
+
"""
|
|
230
|
+
payload: dict[str, Any] = {
|
|
231
|
+
"from": from_currency,
|
|
232
|
+
"to": to_currency,
|
|
233
|
+
"amountDeposit": str(amount_deposit),
|
|
234
|
+
"addressReceive": address_receive,
|
|
235
|
+
}
|
|
236
|
+
if extra_id_receive is not None:
|
|
237
|
+
payload["extraIdReceive"] = extra_id_receive
|
|
238
|
+
if refund_address is not None:
|
|
239
|
+
payload["refundAddress"] = refund_address
|
|
240
|
+
if refund_extra_id is not None:
|
|
241
|
+
payload["refundExtraId"] = refund_extra_id
|
|
242
|
+
if quota_id is not None:
|
|
243
|
+
payload["quotaId"] = quota_id
|
|
244
|
+
if no_quota_id is not None:
|
|
245
|
+
payload["noQuotaId"] = no_quota_id
|
|
246
|
+
if from_network is not None:
|
|
247
|
+
payload["fromNetwork"] = from_network
|
|
248
|
+
if to_network is not None:
|
|
249
|
+
payload["toNetwork"] = to_network
|
|
250
|
+
if offer_reference_id is not None:
|
|
251
|
+
payload["offerReferenceId"] = offer_reference_id
|
|
252
|
+
|
|
253
|
+
return self._request("POST", "/v1/exchange/create", json_data=payload)
|
|
254
|
+
|
|
255
|
+
def get_transaction(self, transaction_id: str) -> TransactionStatus:
|
|
256
|
+
"""Retrieves transaction status.
|
|
257
|
+
|
|
258
|
+
Args:
|
|
259
|
+
transaction_id: Swapzone transaction ID.
|
|
260
|
+
|
|
261
|
+
Returns:
|
|
262
|
+
Transaction details and status.
|
|
263
|
+
|
|
264
|
+
Examples:
|
|
265
|
+
>>> status = client.get_transaction("tx-id")
|
|
266
|
+
>>> print(status["status"])
|
|
267
|
+
"""
|
|
268
|
+
return self._request("GET", "/v1/exchange/tx", params={"id": transaction_id})
|
|
269
|
+
|
|
270
|
+
def validate_address(
|
|
271
|
+
self,
|
|
272
|
+
currency: str,
|
|
273
|
+
address: str,
|
|
274
|
+
network: str | None = None,
|
|
275
|
+
) -> ValidateAddressResult:
|
|
276
|
+
"""Validates a cryptocurrency address for a given asset.
|
|
277
|
+
|
|
278
|
+
Args:
|
|
279
|
+
currency: Asset ticker (e.g. 'eth').
|
|
280
|
+
address: Wallet address to validate.
|
|
281
|
+
network: Blockchain network (e.g. 'ETH').
|
|
282
|
+
|
|
283
|
+
Returns:
|
|
284
|
+
Dictionary indicating if the address is valid.
|
|
285
|
+
|
|
286
|
+
Examples:
|
|
287
|
+
>>> res = client.validate_address("eth", "0x57f3...", network="ETH")
|
|
288
|
+
>>> print(res["result"])
|
|
289
|
+
"""
|
|
290
|
+
params: dict[str, Any] = {
|
|
291
|
+
"currency": currency,
|
|
292
|
+
"address": address,
|
|
293
|
+
}
|
|
294
|
+
if network is not None:
|
|
295
|
+
params["network"] = network
|
|
296
|
+
|
|
297
|
+
return self._request("GET", "/v1/exchange/validate/address", params=params)
|
|
298
|
+
|
|
299
|
+
def get_markets(self, rate_type: str | None = None) -> list[Any]:
|
|
300
|
+
"""Retrieves bulk rate markets for instant exchange pairs.
|
|
301
|
+
|
|
302
|
+
Args:
|
|
303
|
+
rate_type: Optional rate type filter ('all', 'fixed', 'floating').
|
|
304
|
+
|
|
305
|
+
Returns:
|
|
306
|
+
List of market pairs.
|
|
307
|
+
|
|
308
|
+
Examples:
|
|
309
|
+
>>> markets = client.get_markets("fixed")
|
|
310
|
+
"""
|
|
311
|
+
params = {"rateType": rate_type} if rate_type else None
|
|
312
|
+
return self._request("GET", "/v1/exchange/markets", params=params)
|
|
313
|
+
|
|
314
|
+
def get_dex_blockchains(self) -> list[DEXBlockchain]:
|
|
315
|
+
"""Retrieves supported blockchain networks for decentralized swaps.
|
|
316
|
+
|
|
317
|
+
Returns:
|
|
318
|
+
List of supported blockchains with chain IDs.
|
|
319
|
+
|
|
320
|
+
Examples:
|
|
321
|
+
>>> chains = client.get_dex_blockchains()
|
|
322
|
+
>>> print(len(chains))
|
|
323
|
+
"""
|
|
324
|
+
res = self._request("GET", "/v1/dex/blockchains")
|
|
325
|
+
if isinstance(res, dict) and "result" in res:
|
|
326
|
+
return res["result"]
|
|
327
|
+
return res
|
|
328
|
+
|
|
329
|
+
def get_dex_tokens(self) -> list[Any]:
|
|
330
|
+
"""Retrieves supported tokens for DEX swaps.
|
|
331
|
+
|
|
332
|
+
Returns:
|
|
333
|
+
List of supported tokens.
|
|
334
|
+
|
|
335
|
+
Examples:
|
|
336
|
+
>>> tokens = client.get_dex_tokens()
|
|
337
|
+
"""
|
|
338
|
+
res = self._request("GET", "/v1/dex/tokens")
|
|
339
|
+
if isinstance(res, dict) and "result" in res:
|
|
340
|
+
return res["result"]
|
|
341
|
+
return res
|
swapzone/errors.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class SwapzoneError(Exception):
|
|
5
|
+
"""Exception raised when a Swapzone API call fails."""
|
|
6
|
+
|
|
7
|
+
def __init__(
|
|
8
|
+
self,
|
|
9
|
+
message: str,
|
|
10
|
+
status_code: int | None = None,
|
|
11
|
+
response_body: Any = None,
|
|
12
|
+
) -> None:
|
|
13
|
+
super().__init__(message)
|
|
14
|
+
self.message = message
|
|
15
|
+
self.status_code = status_code
|
|
16
|
+
self.response_body = response_body
|
|
17
|
+
|
|
18
|
+
def __str__(self) -> str:
|
|
19
|
+
if self.status_code is not None:
|
|
20
|
+
return f"[{self.status_code}] {self.message}"
|
|
21
|
+
return self.message
|
swapzone/types.py
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
from typing_extensions import TypedDict
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class SupportedCurrency(TypedDict, total=False):
|
|
7
|
+
name: str
|
|
8
|
+
ticker: str
|
|
9
|
+
network: str
|
|
10
|
+
defaultAmount: float
|
|
11
|
+
smartContract: str | None
|
|
12
|
+
alias: list[str]
|
|
13
|
+
hasExternalId: bool
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class RateOffer(TypedDict, total=False):
|
|
17
|
+
adapter: str
|
|
18
|
+
from_: str
|
|
19
|
+
fromNetwork: str
|
|
20
|
+
to: str
|
|
21
|
+
toNetwork: str
|
|
22
|
+
amountFrom: float
|
|
23
|
+
amountTo: float
|
|
24
|
+
amountFromUSD: float
|
|
25
|
+
amountToUSD: float
|
|
26
|
+
quotaId: str
|
|
27
|
+
minAmount: float
|
|
28
|
+
maxAmount: float
|
|
29
|
+
time: int
|
|
30
|
+
accuracyRate: float
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class TransactionResult(TypedDict, total=False):
|
|
34
|
+
id: str
|
|
35
|
+
quotaId: str
|
|
36
|
+
status: str
|
|
37
|
+
addressReceive: str
|
|
38
|
+
addressDeposit: str
|
|
39
|
+
extraIdReceive: str
|
|
40
|
+
amountDeposit: float
|
|
41
|
+
amountEstimated: float
|
|
42
|
+
createdAt: str
|
|
43
|
+
refundAddress: str
|
|
44
|
+
refundExtraId: str
|
|
45
|
+
payTill: str
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class TransactionStatus(TypedDict, total=False):
|
|
49
|
+
id: str
|
|
50
|
+
status: str
|
|
51
|
+
amountDeposit: float
|
|
52
|
+
amountEstimated: float
|
|
53
|
+
addressDeposit: str
|
|
54
|
+
addressReceive: str
|
|
55
|
+
hashDeposit: str | None
|
|
56
|
+
hashReceive: str | None
|
|
57
|
+
createdAt: str
|
|
58
|
+
updatedAt: str
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class ValidateAddressResult(TypedDict, total=False):
|
|
62
|
+
result: bool
|
|
63
|
+
message: str | None
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class DEXBlockchain(TypedDict, total=False):
|
|
67
|
+
chainId: int
|
|
68
|
+
name: str
|
|
69
|
+
nativeCurrency: dict[str, Any]
|
|
70
|
+
rpcUrls: list[str]
|
|
71
|
+
explorerUrls: list[str]
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: swapzone
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python client for the Swapzone cryptocurrency exchange and DEX aggregator API
|
|
5
|
+
Project-URL: Repository, https://github.com/TysikGG/api-kit
|
|
6
|
+
Project-URL: Documentation, https://documenter.getpostman.com/view/16362858/UVXokDS6
|
|
7
|
+
License-Expression: ISC
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Requires-Python: >=3.10
|
|
10
|
+
Requires-Dist: httpx>=0.27
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
|
|
13
|
+
# swapzone
|
|
14
|
+
|
|
15
|
+
[](https://pypi.org/project/swapzone/)
|
|
16
|
+
[](LICENSE)
|
|
17
|
+
[](https://pypi.org/project/swapzone/)
|
|
18
|
+
|
|
19
|
+
Modern, fully typed Python client library for the [Swapzone](https://swapzone.io) instant cryptocurrency exchange & DEX cross-chain aggregator REST API.
|
|
20
|
+
|
|
21
|
+
Official API documentation: <https://documenter.getpostman.com/view/16362858/UVXokDS6>
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Features
|
|
26
|
+
|
|
27
|
+
- 🔄 **15+ Exchange Providers Aggregated** — Compares rates in real time across top instant swap platforms (SimpleSwap, StealthEX, ChangeNOW, Changelly, etc.).
|
|
28
|
+
- 🪙 **1000+ Cryptocurrencies** — Full support for fixed and floating cross-chain swaps.
|
|
29
|
+
- 🐍 **Modern Pythonic Design** — Type annotations, dataclass models (`RateOffer`, `TransactionResult`, `SupportedCurrency`, `TransactionStatus`), context manager support (`with Swapzone(...) as client:`), and custom exceptions.
|
|
30
|
+
- ⚡ **DEX Aggregation** — DEX blockchain routes, token catalogs, and quote comparisons.
|
|
31
|
+
- 🛡️ **Address Validation** — Built-in `validate_address()` helper to verify destination wallet addresses.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Installation
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install swapzone
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Requires Python 3.10 or higher.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## Quick Start (Safe Check)
|
|
46
|
+
|
|
47
|
+
The quick check only inspects supported currencies and compares live provider exchange rates. It does not create orders:
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
import os
|
|
51
|
+
from swapzone import Swapzone
|
|
52
|
+
|
|
53
|
+
with Swapzone(api_key=os.environ["SWAPZONE_API_KEY"]) as client:
|
|
54
|
+
# 1. Fetch supported currencies
|
|
55
|
+
currencies = client.get_currencies()
|
|
56
|
+
print(f"Available currencies: {len(currencies)}")
|
|
57
|
+
|
|
58
|
+
# 2. Query best rate offer for 0.1 BTC to ETH
|
|
59
|
+
rate = client.get_rate(
|
|
60
|
+
from_currency="btc",
|
|
61
|
+
to_currency="eth",
|
|
62
|
+
amount=0.1,
|
|
63
|
+
rate_type="all",
|
|
64
|
+
)
|
|
65
|
+
print(f"Best Provider: {rate.best_offer.adapter}")
|
|
66
|
+
print(f"Estimated Receive: {rate.best_offer.amount_to} ETH")
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Usage Guide
|
|
72
|
+
|
|
73
|
+
### 1. Finding the Best Rate and Creating a Swap
|
|
74
|
+
|
|
75
|
+
Full lifecycle: query provider rates, validate destination address, create transaction, and monitor status:
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
from swapzone import Swapzone, SwapzoneError
|
|
79
|
+
|
|
80
|
+
with Swapzone(api_key="your_api_key") as client:
|
|
81
|
+
# 1. Get rate offers across all providers
|
|
82
|
+
rate = client.get_rate(
|
|
83
|
+
from_currency="btc",
|
|
84
|
+
to_currency="eth",
|
|
85
|
+
amount=0.1,
|
|
86
|
+
rate_type="all",
|
|
87
|
+
)
|
|
88
|
+
best_offer = rate.best_offer
|
|
89
|
+
print(f"Selected Provider: {best_offer.adapter} (Quota: {best_offer.amount_to} ETH)")
|
|
90
|
+
|
|
91
|
+
# 2. Validate destination wallet address
|
|
92
|
+
check = client.validate_address(
|
|
93
|
+
currency="eth",
|
|
94
|
+
address="0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
|
|
95
|
+
)
|
|
96
|
+
if not check.result:
|
|
97
|
+
raise ValueError("Invalid destination wallet address!")
|
|
98
|
+
|
|
99
|
+
# 3. Create transaction with the selected provider
|
|
100
|
+
tx = client.create_transaction(
|
|
101
|
+
from_currency="btc",
|
|
102
|
+
to_currency="eth",
|
|
103
|
+
amount_deposit=0.1,
|
|
104
|
+
address_receive="0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
|
|
105
|
+
quota_id=best_offer.quota_id,
|
|
106
|
+
refund_address="bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh", # optional
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
print(f"Transaction Created! ID: {tx.transaction.id}")
|
|
110
|
+
print(f"Send BTC to Deposit Address: {tx.transaction.address_deposit}")
|
|
111
|
+
print(f"Status: {tx.transaction.status}")
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### 2. Tracking Transaction Progress
|
|
115
|
+
|
|
116
|
+
Monitor swap progress from deposit to settlement:
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
current = client.get_transaction(tx.transaction.id)
|
|
120
|
+
print(f"Status: {current.status}")
|
|
121
|
+
|
|
122
|
+
if current.status == "finished":
|
|
123
|
+
print(f"Received: {current.amount_receive} ETH")
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### 3. Querying DEX Aggregation
|
|
127
|
+
|
|
128
|
+
Inspect supported DEX blockchains and token pairs:
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
# List supported DEX blockchains
|
|
132
|
+
blockchains = client.get_dex_blockchains()
|
|
133
|
+
print("DEX Blockchains:", blockchains)
|
|
134
|
+
|
|
135
|
+
# Check DEX tokens
|
|
136
|
+
dex_tokens = client.get_dex_tokens()
|
|
137
|
+
print(f"DEX Tokens count: {len(dex_tokens)}")
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## API Reference
|
|
143
|
+
|
|
144
|
+
### `Swapzone` Class
|
|
145
|
+
|
|
146
|
+
#### Constructor
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
Swapzone(
|
|
150
|
+
api_key: str,
|
|
151
|
+
base_url: str = "https://api.swapzone.io/v1",
|
|
152
|
+
timeout: float = 30.0,
|
|
153
|
+
)
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
| Parameter | Type | Default | Description |
|
|
157
|
+
| :--- | :--- | :--- | :--- |
|
|
158
|
+
| `api_key` | `str` | **Required** | Your Swapzone API key. |
|
|
159
|
+
| `base_url` | `str` | `"https://api.swapzone.io/v1"` | Swapzone API base URL. |
|
|
160
|
+
| `timeout` | `float` | `30.0` | HTTP request timeout in seconds. |
|
|
161
|
+
|
|
162
|
+
#### Methods
|
|
163
|
+
|
|
164
|
+
- **`get_currencies() -> list[SupportedCurrency]`** — Returns all supported cryptocurrencies.
|
|
165
|
+
- **`get_rate(from_currency, to_currency, amount, rate_type="all") -> RateOffer`** — Returns aggregated quotes.
|
|
166
|
+
- **`create_transaction(from_currency, to_currency, amount_deposit, address_receive, quota_id, ...) -> TransactionResult`** — Creates transaction.
|
|
167
|
+
- **`get_transaction(transaction_id: str) -> TransactionStatus`** — Checks swap status and execution details.
|
|
168
|
+
- **`validate_address(currency, address) -> ValidateAddressResult`** — Validates destination wallet address.
|
|
169
|
+
- **`get_markets(rate_type=None) -> list[Any]`** — Retrieves active market pairs.
|
|
170
|
+
- **`get_dex_blockchains() -> list[DEXBlockchain]`** — Lists supported DEX blockchains.
|
|
171
|
+
- **`get_dex_tokens() -> list[Any]`** — Lists tokens supported on DEX routers.
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## Error Handling
|
|
176
|
+
|
|
177
|
+
When the API returns an error, `SwapzoneError` is raised:
|
|
178
|
+
|
|
179
|
+
```python
|
|
180
|
+
from swapzone import Swapzone, SwapzoneError
|
|
181
|
+
|
|
182
|
+
try:
|
|
183
|
+
with Swapzone(api_key="invalid_key") as client:
|
|
184
|
+
client.get_currencies()
|
|
185
|
+
except SwapzoneError as exc:
|
|
186
|
+
print(f"Swapzone Error: {exc.message} (Status: {exc.status})")
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Common HTTP status codes:
|
|
190
|
+
- `400 Bad Request` — Invalid address or amount out of limits.
|
|
191
|
+
- `401 Unauthorized` — Invalid API key.
|
|
192
|
+
- `404 Not Found` — Transaction ID not found.
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## License
|
|
197
|
+
|
|
198
|
+
ISC
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
swapzone/__init__.py,sha256=EeGsFbxT6hdtwVbVFbiZd6dx4yVLK4mDPDh4HgLsQiU,477
|
|
2
|
+
swapzone/client.py,sha256=O95Pnqo8H2gululdU-vH0FrZPmeagDHLFRw-xL3Scls,11677
|
|
3
|
+
swapzone/errors.py,sha256=L9Kt1Qq__Kp2NzbcAhWtQOfbRetq_HnWQMrrAH8akW4,566
|
|
4
|
+
swapzone/types.py,sha256=KTygOSJLN3tYk2x7lfQtv7e0lu9q8MGSgAqDibG47P4,1411
|
|
5
|
+
swapzone-0.1.0.dist-info/METADATA,sha256=A3S6sIU96Z8vQH2z8f41PzSqwSZu4FeQIw9FgM0srnE,6343
|
|
6
|
+
swapzone-0.1.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
|
|
7
|
+
swapzone-0.1.0.dist-info/licenses/LICENSE,sha256=8S0uYmn_aeljwFudfBoV91HcIo79yab9s0yhMVsOHJ0,751
|
|
8
|
+
swapzone-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 api-kit contributors
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
7
|
+
copyright notice and this permission notice appear in all copies.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
10
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
11
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
12
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
13
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
14
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
15
|
+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|