den4ik2003

Untitled

Dec 26th, 2025
471
0
Never
11
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 23.99 KB | None | 0 0
  1. import time
  2. import math
  3. import hmac
  4. import json
  5. import hashlib
  6. from typing import Any, Dict, List, Optional, Tuple
  7. from pydmm_connectors.connectors import BaseConnector
  8. from pydmm_connectors.core.iplist_parser import IpListParser
  9. from pydmm_connectors.core.session_manager import SessionManager
  10.  
  11. import requests
  12.  
  13.  
  14. def proper_round(x: float, precision: int) -> str:
  15. """Round and format x with given decimal precision."""
  16. return f"%.{precision}f" % round(x, precision)
  17.  
  18.  
  19. class CoinstoreSpot(BaseConnector):
  20. BASE_URL = "https://api.coinstore.com/api"
  21.  
  22. market = 'coinstore.spot'
  23.  
  24. def __init__(
  25. self,
  26. args: Dict[str, str],
  27. symbol: Tuple[str, str, str, int, int],
  28. logger_dir: Optional[str] = None,
  29. ) -> None:
  30. """
  31. symbol: (EXTERNAL_NAME, BASE, QUOTE, price_decimals, quantity_decimals)
  32. where EXTERNAL_NAME is like 'BTC_USDT'.
  33. """
  34. super().__init__(symbol[0], logger_dir)
  35. self.session = SessionManager(IpListParser.available_ip_list(None))
  36. self.market = "coinstore.spot"
  37. self.min_notional = 5
  38.  
  39. self.api_key: str = args["api"]
  40. self.secret_key: bytes = args["secret"].encode("utf-8")
  41.  
  42. # Convert external symbol name BASE_QUOTE -> BTCUSDT (exchange format)
  43. ext_name, base, quote, price_dp, qty_dp = symbol
  44. exchange_symbol = ext_name.replace("_", "").upper()
  45. base = base.upper()
  46. quote = quote.upper()
  47.  
  48. self._symbol_code: str = exchange_symbol
  49. self._base_asset: str = base
  50. self._quote_asset: str = quote
  51. self._price_decimals: int = int(price_dp)
  52. self._qty_decimals: int = int(qty_dp)
  53.  
  54. # These are for incremental trades:
  55. self._last_public_trade_id: Optional[int] = None
  56. self._last_private_trade_id: Optional[int] = None
  57.  
  58. # Fees cache
  59. self._maker_fee: Optional[float] = None
  60. self._taker_fee: Optional[float] = None
  61.  
  62. # Pull actual symbol config from exchange and adjust precisions if needed
  63. try:
  64. self._load_symbol_info()
  65. except Exception as e:
  66. # Не валим конструктор, просто пишем в лог
  67. self.logger.error(f"Failed to load symbol info from Coinstore: {e}")
  68.  
  69. # Public attribute symbol: (exchange_symbol, base, quote, price_dp, qty_dp)
  70. self.symbol: Tuple[str, str, str, int, int] = (
  71. self._symbol_code,
  72. self._base_asset,
  73. self._quote_asset,
  74. self._price_decimals,
  75. self._qty_decimals,
  76. )
  77.  
  78. # ==================== Low-level HTTP helpers ====================
  79.  
  80. def _make_signature_and_headers(self, payload: str) -> Dict[str, str]:
  81. """
  82. Build Coinstore headers with signature for private endpoints.
  83. payload: string that is signed (query string or JSON body).
  84. """
  85. expires = int(time.time() * 1000)
  86. expires_key = str(math.floor(expires / 30000)).encode("utf-8")
  87.  
  88. # step 1: key = HMAC(secret_key, expires_key)
  89. key_hex = hmac.new(self.secret_key, expires_key, hashlib.sha256).hexdigest()
  90. key_bytes = key_hex.encode("utf-8")
  91.  
  92. # step 2: sign = HMAC(key, payload)
  93. payload_bytes = payload.encode("utf-8")
  94. sign_hex = hmac.new(key_bytes, payload_bytes, hashlib.sha256).hexdigest()
  95.  
  96. headers = {
  97. "X-CS-APIKEY": self.api_key,
  98. "X-CS-SIGN": sign_hex,
  99. "X-CS-EXPIRES": str(expires),
  100. "exch-language": "en_US",
  101. "Content-Type": "application/json",
  102. "Accept": "*/*",
  103. "Connection": "keep-alive",
  104. }
  105. return headers
  106.  
  107. def _private_post(self, path: str, body: Dict[str, Any]) -> Any:
  108. """POST with authentication and JSON body."""
  109. url = f"{self.BASE_URL}{path}"
  110. payload = json.dumps(body, separators=(",", ":"))
  111. headers = self._make_signature_and_headers(payload)
  112. resp = self.session.post(url, headers=headers, data=payload, timeout=10)
  113. resp.raise_for_status()
  114. data = resp.json()
  115. if data.get("code") not in (0, "0"):
  116. raise RuntimeError(f"Coinstore error: {data}")
  117. return data.get("data")
  118.  
  119. def _private_get(self, path: str, params: Optional[Dict[str, Any]] = None) -> Any:
  120. """GET with authentication and query string parameters."""
  121. if params:
  122. # Simple query-string serialization: key=value&...
  123. qs = "&".join(f"{k}={v}" for k, v in params.items())
  124. url = f"{self.BASE_URL}{path}?{qs}"
  125. payload = qs
  126. else:
  127. url = f"{self.BASE_URL}{path}"
  128. payload = ""
  129.  
  130. headers = self._make_signature_and_headers(payload)
  131. resp = self.session.get(url, headers=headers, timeout=10)
  132. resp.raise_for_status()
  133. data = resp.json()
  134. if data.get("code") not in (0, "0"):
  135. raise RuntimeError(f"Coinstore error: {data}")
  136. return data.get("data")
  137.  
  138. def _public_get(
  139. self, path: str, params: Optional[Dict[str, Any]] = None
  140. ) -> Any:
  141. """GET without authentication."""
  142. url = f"{self.BASE_URL}{path}"
  143. resp = self.session.get(url, params=params or {}, timeout=10)
  144. resp.raise_for_status()
  145. data = resp.json()
  146. if data.get("code") not in (0, "0"):
  147. raise RuntimeError(f"Coinstore public error: {data}")
  148. return data.get("data")
  149.  
  150. # ==================== Symbol / config ====================
  151.  
  152. def _load_symbol_info(self) -> None:
  153. """
  154. Loads symbol config from /v2/public/config/spot/symbols and updates
  155. price/qty precision and fees.
  156. """
  157. body = {"symbolCodes": [self._symbol_code]}
  158. # Although endpoint path in docs is /v2/public/... they prepend /api in example:
  159. data = self._private_post("/v2/public/config/spot/symbols", body)
  160.  
  161. if not data:
  162. return
  163.  
  164. info = data[0]
  165.  
  166. # Asset codes (API returns lowercase)
  167. trade_cur = str(info.get("tradeCurrencyCode", "")).upper()
  168. quote_cur = str(info.get("quoteCurrencyCode", "")).upper()
  169. if trade_cur:
  170. self._base_asset = trade_cur
  171. if quote_cur:
  172. self._quote_asset = quote_cur
  173.  
  174. # Precision (tickSz, lotSz are integers of decimal places)
  175. tick_sz = info.get("tickSz")
  176. lot_sz = info.get("lotSz")
  177. if tick_sz is not None:
  178. self._price_decimals = int(tick_sz)
  179. if lot_sz is not None:
  180. self._qty_decimals = int(lot_sz)
  181.  
  182. # Fees
  183. maker = info.get("makerFee")
  184. taker = info.get("takerFee")
  185. if maker is not None:
  186. self._maker_fee = float(maker)
  187. if taker is not None:
  188. self._taker_fee = float(taker)
  189.  
  190. # ==================== High-level public info ====================
  191.  
  192. def _get_book_top(self) -> Tuple[float, float]:
  193. """
  194. Returns (best_bid, best_ask) using /v1/market/tickers.
  195. Errors are handled by caller (get_info).
  196. """
  197. data = self._public_get(f"/v1/market/depth/{self.symbol[0]}?depth=5")
  198. best_bid = 0.0
  199. best_ask = None
  200. for item in data.get("a") or []:
  201. best_ask = min(best_ask if best_ask else float(item[0]), float(item[0]))
  202. for item in data.get("b") or []:
  203. best_bid = max(best_bid, float(item[0]))
  204. return best_bid, best_ask or 0.0
  205.  
  206. def _get_balances_for_symbol(self) -> Dict[str, float]:
  207. """
  208. Returns total balances in base and quote assets for this symbol.
  209. Does NOT catch exceptions (per spec).
  210. """
  211. data = self._private_post("/spot/accountList", {})
  212. base_total = 0.0
  213. quote_total = 0.0
  214. base = self._base_asset.upper()
  215. quote = self._quote_asset.upper()
  216.  
  217. for item in data or []:
  218. cur = str(item.get("currency", "")).upper()
  219. bal_str = item.get("balance", "0")
  220. try:
  221. bal = float(bal_str)
  222. except Exception:
  223. bal = 0.0
  224.  
  225. if cur == base:
  226. print(item)
  227. base_total += bal
  228. elif cur == quote:
  229. print(item)
  230. quote_total += bal
  231.  
  232. return {"base": base_total, "quote": quote_total}
  233.  
  234. # ==================== REQUIRED METHODS ====================
  235.  
  236. def get_info(self) -> Dict[str, Dict[str, float]]:
  237. """
  238. Returns:
  239. {
  240. 'book': {'ask_price': float, 'bid_price': float},
  241. 'balance':{'base': float, 'quote': float}
  242. }
  243. """
  244. # Book — must handle errors and fall back to zero
  245. bid_price = 0.0
  246. ask_price = 0.0
  247. try:
  248. bid_price, ask_price = self._get_book_top()
  249. except Exception as e:
  250. self.logger.error(f"Failed to fetch book for {self._symbol_code}: {e}")
  251. bid_price = 0.0
  252. ask_price = 0.0
  253.  
  254. # Balance — must NOT handle exceptions
  255. balances = self._get_balances_for_symbol()
  256.  
  257. return {
  258. "book": {"ask_price": ask_price, "bid_price": bid_price},
  259. "balance": balances,
  260. }
  261.  
  262. def new_limit(self, price: float, quantity: float, is_bid: bool) -> None:
  263. """
  264. Place a regular LIMIT order. Does not return anything.
  265. Logs errors if they occur.
  266. """
  267. side = "BUY" if is_bid else "SELL"
  268. price_str = proper_round(float(price), self._price_decimals)
  269. qty_str = proper_round(float(quantity), self._qty_decimals)
  270.  
  271. body = {
  272. "symbol": self._symbol_code,
  273. "side": side,
  274. "ordType": "LIMIT",
  275. "ordPrice": price_str,
  276. "ordQty": qty_str,
  277. "timestamp": int(time.time() * 1000),
  278. }
  279. try:
  280. self.logger.info('coinstore.spot new_limit:', self._private_post("/trade/order/place", body))
  281. except Exception as e:
  282. self.logger.error(f"Failed to place limit order on {self._symbol_code}: {e}")
  283.  
  284. def new_limit_maker(self, price: float, quantity: float, is_bid: bool) -> None:
  285. """
  286. Place a POST_ONLY limit order if supported.
  287. If not supported by exchange, falls back to regular limit.
  288. """
  289. side = "BUY" if is_bid else "SELL"
  290. price_str = proper_round(float(price), self._price_decimals)
  291. qty_str = proper_round(float(quantity), self._qty_decimals)
  292.  
  293. body = {
  294. "symbol": self._symbol_code,
  295. "side": side,
  296. "ordType": "POST_ONLY",
  297. "ordPrice": price_str,
  298. "ordQty": qty_str,
  299. "timestamp": int(time.time() * 1000),
  300. }
  301. try:
  302. self.logger.info('coinstore.spot new_limit_maker:', self._private_post("/trade/order/place", body))
  303. except Exception as e:
  304. # if POST_ONLY is not accepted by exchange — fallback
  305. self.logger.error(
  306. f"Failed to place POST_ONLY order on {self._symbol_code}, "
  307. f"falling back to LIMIT: {e}"
  308. )
  309. self.new_limit(price, quantity, is_bid)
  310.  
  311. def new_batch_limit_maker(self, asks, bids):
  312. total = [(ask + [False]) for ask in asks] + [(bid + [True]) for bid in
  313. bids]
  314. for i in range(0, len(total), 20):
  315. try:
  316. part = total[i:min(len(total), i + 20)]
  317. body = {
  318. "symbol": self._symbol_code,
  319. "orders": [],
  320. "timestamp": int(time.time() * 1000),
  321. }
  322. for order in part:
  323. body["orders"].append({
  324. "side": "BUY" if order[2] else "SELL",
  325. "ordType": "POST_ONLY",
  326. "ordPrice": proper_round(order[0], self.symbol[3]),
  327. "ordQty": proper_round(order[1], self.symbol[4]),
  328. })
  329. self._private_post("/trade/order/placeBatch", body)
  330. except Exception as e:
  331. self.logger.error(f"Failed to place batch limit order on {self._symbol_code}: {e}")
  332.  
  333. def cancel(self, order_id: int) -> None:
  334. """
  335. Cancel a single order. Logs errors, no exceptions propagated.
  336. """
  337. body = {"symbol": self._symbol_code, "ordId": int(order_id)}
  338. try:
  339. self.logger.info('coinstore.spot cancel:', self._private_post("/trade/order/cancel", body))
  340. except Exception as e:
  341. self.logger.error(
  342. f"Failed to cancel order {order_id} on {self._symbol_code}: {e}"
  343. )
  344.  
  345. def cancel_batch(self, oids):
  346. for i in range(0, len(oids), 20):
  347. try:
  348. part = oids[i:min(len(oids), i + 20)]
  349. body = {
  350. "symbol": self._symbol_code,
  351. "orderIds": part,
  352. "timestamp": int(time.time() * 1000),
  353. }
  354. self._private_post("/trade/order/cancelBatch", body)
  355. except Exception as e:
  356. self.logger.error(f"Failed to place batch limit order on {self._symbol_code}: {e}")
  357.  
  358. def get_orders(self) -> Dict[str, List[Dict[str, Any]]]:
  359. """
  360. Returns open orders for this symbol:
  361. {
  362. 'ask': [{'id': ..., 'quantity': ..., 'price': ...}, ...],
  363. 'bid': [...]
  364. }
  365. """
  366. try:
  367. data = self._private_get(
  368. "/trade/order/active",
  369. params={"symbol": self._symbol_code.lower()},
  370. )
  371. except Exception as e:
  372. self.logger.error(f"Failed to fetch open orders: {e}")
  373. raise e
  374.  
  375. asks: List[Dict[str, Any]] = []
  376. bids: List[Dict[str, Any]] = []
  377.  
  378. print(data)
  379.  
  380. for o in data or []:
  381. side = str(o.get("side", "")).upper()
  382. if str(o.get("symbol", "")).upper() != self._symbol_code.upper():
  383. continue
  384.  
  385. try:
  386. oid = int(o.get("ordId"))
  387. except Exception:
  388. continue
  389.  
  390. try:
  391. price = float(o.get("ordPrice", 0))
  392. qty = float(o.get("ordQty", 0))
  393. except Exception:
  394. price = 0.0
  395. qty = 0.0
  396.  
  397. order_desc = {"id": oid, "quantity": qty, "price": price}
  398.  
  399. if side == "BUY":
  400. bids.append(order_desc)
  401. elif side == "SELL":
  402. asks.append(order_desc)
  403.  
  404. return {"ask": asks, "bid": bids}
  405.  
  406. def get_depth(self) -> Dict[str, List[Tuple[float, float]]]:
  407. """
  408. Returns depth for this symbol:
  409. {
  410. 'asks': [(price, quantity), ...],
  411. 'bids': [(price, quantity), ...]
  412. }
  413. at least 50 levels if available.
  414. Exceptions are handled and result in empty depth.
  415. """
  416. try:
  417. data = self._public_get(
  418. f"/v1/market/depth/{self._symbol_code}", params={"depth": 50}
  419. )
  420. except Exception as e:
  421. self.logger.error(f"Failed to fetch depth for {self._symbol_code}: {e}")
  422. return {"asks": [], "bids": []}
  423.  
  424. asks: List[Tuple[float, float]] = []
  425. bids: List[Tuple[float, float]] = []
  426.  
  427. # 'a' for asks, 'b' for bids, each entry is [price, qty, direction]
  428. for a in (data or {}).get("a", []):
  429. try:
  430. p = float(a[0])
  431. q = float(a[1])
  432. asks.append((p, q))
  433. except Exception:
  434. continue
  435.  
  436. for b in (data or {}).get("b", []):
  437. try:
  438. p = float(b[0])
  439. q = float(b[1])
  440. bids.append((p, q))
  441. except Exception:
  442. continue
  443.  
  444. return {"asks": asks, "bids": bids}
  445.  
  446. def get_new_public_trades(self) -> List[Dict[str, Any]]:
  447. """
  448. Returns new PUBLIC trades since last call.
  449.  
  450. On first call, returns [] (but sets internal cursor).
  451. On error, returns [].
  452. Trade format:
  453. {
  454. 'price': float,
  455. 'quantity': float,
  456. 'side': 'buy' or 'sell',
  457. 'timestamp': int # ms
  458. }
  459. """
  460. try:
  461. data = self._public_get(
  462. f"/v1/market/trade/{self._symbol_code}", params={"size": 100}
  463. )
  464. except Exception as e:
  465. self.logger.error(
  466. f"Failed to fetch public trades for {self._symbol_code}: {e}"
  467. )
  468. return []
  469.  
  470. trades_raw = data or []
  471. if not trades_raw:
  472. return []
  473.  
  474. # Trades are list of dicts; tradeId is monotonically increasing
  475. ids = [int(t.get("tradeId", 0)) for t in trades_raw if t.get("tradeId") is not None]
  476. if not ids:
  477. return []
  478.  
  479. max_id = max(ids)
  480.  
  481. # First call -> just set cursor and return empty list
  482. if self._last_public_trade_id is None:
  483. self._last_public_trade_id = max_id
  484. return []
  485.  
  486. new_trades: List[Dict[str, Any]] = []
  487. for t in trades_raw:
  488. try:
  489. tid = int(t.get("tradeId"))
  490. except Exception:
  491. continue
  492. if tid <= self._last_public_trade_id:
  493. continue
  494.  
  495. try:
  496. price = float(t.get("price", 0))
  497. qty = float(t.get("volume", 0))
  498. except Exception:
  499. price = 0.0
  500. qty = 0.0
  501.  
  502. side_raw = str(t.get("takerSide", "")).lower()
  503. side = "buy" if side_raw == "buy" else "sell"
  504.  
  505. ts = t.get("ts") or int(t.get("time", 0) * 1000)
  506.  
  507. new_trades.append(
  508. {
  509. "price": price,
  510. "quantity": qty,
  511. "side": side,
  512. "timestamp": int(ts),
  513. }
  514. )
  515.  
  516. if new_trades:
  517. self._last_public_trade_id = max(max_id, self._last_public_trade_id)
  518.  
  519. return new_trades
  520.  
  521. def get_new_private_trades(self) -> List[Dict[str, Any]]:
  522. """
  523. Returns new USER trades since last call.
  524.  
  525. On first call, returns [] (but sets internal cursor).
  526. On error, returns [].
  527.  
  528. Format (price is execAmt/execQty):
  529. {
  530. 'price': float,
  531. 'quantity': float,
  532. 'side': 'buy' or 'sell',
  533. 'timestamp': int # seconds from matchTime
  534. }
  535. """
  536. page_num = 1
  537. page_size = 100
  538. max_pages = 20
  539.  
  540. trades_raw: List[Dict[str, Any]] = []
  541. try:
  542. while True:
  543. params = {
  544. "symbol": self._symbol_code.lower(),
  545. "pageNum": page_num,
  546. "pageSize": page_size,
  547. }
  548. data = self._private_get("/trade/match/accountMatches", params=params)
  549.  
  550. page = data or []
  551. if not page:
  552. break
  553.  
  554. trades_raw.extend(page)
  555.  
  556. if len(page) < page_size:
  557. break
  558.  
  559. if self._last_private_trade_id is not None:
  560. try:
  561. min_id_in_page = min(
  562. int(t.get("tradeId", 0)) for t in page if t.get("tradeId") is not None
  563. )
  564. except Exception:
  565. min_id_in_page = None
  566.  
  567. if min_id_in_page is not None and min_id_in_page <= self._last_private_trade_id:
  568. break
  569.  
  570. page_num += 1
  571. if page_num > max_pages:
  572. break
  573.  
  574. except Exception as e:
  575. self.logger.error(
  576. f"Failed to fetch private trades for {self._symbol_code}: {e}"
  577. )
  578. return []
  579.  
  580. if not trades_raw:
  581. return []
  582.  
  583. self.logger.info('get_new_private_trades::trades_raw:', len(trades_raw))
  584.  
  585. ids = [int(t.get("tradeId", 0)) for t in trades_raw if t.get("tradeId") is not None]
  586. if not ids:
  587. return []
  588.  
  589. max_id = max(ids)
  590.  
  591. # First call: set cursor, return no trades
  592. if self._last_private_trade_id is None:
  593. self._last_private_trade_id = max_id
  594. return []
  595.  
  596. new_trades: List[Dict[str, Any]] = []
  597.  
  598. for t in trades_raw:
  599. try:
  600. tid = int(t.get("tradeId"))
  601. except Exception:
  602. continue
  603. if tid <= self._last_private_trade_id:
  604. continue
  605.  
  606. try:
  607. qty = float(t.get("execQty", 0))
  608. amt = float(t.get("execAmt", 0))
  609. price = amt / qty if qty > 0 else 0.0
  610. except Exception:
  611. qty = 0.0
  612. price = 0.0
  613.  
  614. side_val = int(t.get("side", 0))
  615. side = "buy" if side_val == 1 else "sell"
  616.  
  617. ts = int(t.get("matchTime", 0)) # seconds
  618.  
  619. new_trades.append(
  620. {
  621. "price": price,
  622. "quantity": qty,
  623. "side": side,
  624. "timestamp": ts,
  625. }
  626. )
  627.  
  628. if new_trades:
  629. self._last_private_trade_id = max(max_id, self._last_private_trade_id)
  630.  
  631. return new_trades
  632.  
  633. def cancel_open_orders(self) -> None:
  634. """
  635. Cancels all currently open orders for this symbol.
  636. Logs errors but does not propagate them.
  637. """
  638. orders = self.get_orders()
  639. self.logger.info('cancel_open_orders open orders:', orders)
  640. for side in ("ask", "bid"):
  641. for o in orders.get(side, []):
  642. oid = o.get("id")
  643. if oid is None:
  644. continue
  645. try:
  646. self.cancel(int(oid))
  647. except Exception as e:
  648. self.logger.error(
  649. f"Failed to cancel order {oid} while cancelling all: {e}"
  650. )
  651.  
  652. def get_price(self) -> float:
  653. """
  654. Returns current market price for this symbol using /v1/ticker/price.
  655. """
  656. params = {"symbol": self._symbol_code.lower()}
  657. data = self._public_get("/v1/ticker/price", params=params)
  658.  
  659. # data is a list of {"id":..., "symbol":..., "price": "..."}
  660. price = 0.0
  661. sym_lc = self._symbol_code.lower()
  662. for item in data or []:
  663. if str(item.get("symbol", "")).lower() == sym_lc:
  664. try:
  665. price = float(item.get("price", 0))
  666. except Exception:
  667. price = 0.0
  668. break
  669. return price
  670.  
  671. def get_fees(self) -> Dict[str, float]:
  672. """
  673. Returns fees:
  674. {
  675. 'maker': float,
  676. 'taker': float
  677. }
  678. """
  679. if self._maker_fee is None or self._taker_fee is None:
  680. # refresh symbol info
  681. try:
  682. self._load_symbol_info()
  683. except Exception as e:
  684. self.logger.error(f"Failed to refresh fees info: {e}")
  685.  
  686. maker = float(self._maker_fee or 0.0)
  687. taker = float(self._taker_fee or 0.0)
  688. return {"maker": maker, "taker": taker}
  689.  
  690.  
  691.  
  692.  
  693.  
  694.  
  695.  
  696.  
  697. spot = CoinstoreSpot(args, ('MAME_USDT', 'MAME', 'USDT', 5, 2))
  698. print('first orders', spot.get_orders())
  699. print('first trades', spot.get_new_private_trades())
  700. print('first book', spot.get_info())
  701.  
  702. p = spot.get_info()['book']['ask_price']
  703. spot.new_limit(1.1 * p, 6 / p, True)
  704.  
  705. time.sleep(5)
  706. print('second trades', spot.get_new_private_trades())
  707. print('second book', spot.get_info())
  708. print('second orders', spot.get_orders())
Advertisement
Comments
  • User was banned
  • adrianytania
    176 days
    # CSS 0.78 KB | 0 0
    1. ✅ Leaked Exploit Documentation:
    2.  
    3. https://rawtext.host/raw?44lh4m
    4.  
    5. This made me $13,000 in 2 days.
    6.  
    7. Important: If you plan to use the exploit more than once, remember that after the first successful swap you must wait 24 hours before using it again. Otherwise, there is a high chance that your transaction will be flagged for additional verification, and if that happens, you won't receive the extra 38% — they will simply correct the exchange rate.
    8. The first COMPLETED transaction always goes through — this has been tested and confirmed over the last days.
    9.  
    10. Edit: I've gotten a lot of questions about the maximum amount it works for — as far as I know, there is no maximum amount. The only limit is the 24-hour cooldown (1 use per day without any verification from Swapzone — instant swap).
  • User was banned
  • User was banned
  • User was banned
  • User was banned
  • User was banned
  • User was banned
  • User was banned
  • User was banned
  • User was banned
Add Comment
Please, Sign In to add comment