Skip to content

Commit b6c0ce2

Browse files
Paulclaude
authored andcommitted
Add WizardSwap exchange implementation (TS source + examples)
No-account, no-API-key cryptocurrency swap service with own liquidity pool. Implements fetchCurrencies, fetchTicker (via estimate), createOrder, and fetchOrder. Fixed 2.2% fee, floating rate. Python/PHP/Go/C# to be generated by running: npm run export-exchanges && npm run emitAPI wizardswap && npm run transpileRest wizardswap Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 76499d9 commit b6c0ce2

7 files changed

Lines changed: 908 additions & 0 deletions
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# WizardSwap — create a swap order and poll until finished
2+
# No API key needed
3+
#
4+
# ⚠ This example actually creates a real swap.
5+
# You must send the exact deposit amount within 15 minutes.
6+
#
7+
# Usage:
8+
# python examples/py/wizardswap-create-swap.py <BTC_ADDRESS> [XMR_AMOUNT]
9+
#
10+
# Example:
11+
# python examples/py/wizardswap-create-swap.py bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh 0.5
12+
13+
import sys
14+
import time
15+
import ccxt
16+
17+
18+
def main():
19+
if len(sys.argv) < 2:
20+
print('Usage: python examples/py/wizardswap-create-swap.py <BTC_ADDRESS> [XMR_AMOUNT]')
21+
sys.exit(1)
22+
23+
btc_address = sys.argv[1]
24+
xmr_amount = float(sys.argv[2]) if len(sys.argv) > 2 else 0.1
25+
26+
exchange = ccxt.wizardswap()
27+
exchange.load_markets()
28+
29+
# 1. Estimate
30+
print(f'Estimating {xmr_amount} XMR -> BTC …')
31+
ticker = exchange.fetch_ticker('XMR/BTC', {'amount_from': str(xmr_amount)})
32+
print(f' Estimated receive: ~{ticker["last"]} BTC (2.2% fee included)\n')
33+
34+
# 2. Create exchange
35+
print('Creating swap order…')
36+
order = exchange.create_order('XMR/BTC', 'market', 'sell', xmr_amount, None, {
37+
'address_to': btc_address,
38+
})
39+
40+
print(f' Order ID: {order["id"]}')
41+
print(f' Deposit address: {order["info"]["address_from"]}')
42+
if order['info'].get('extra_id_from'):
43+
print(f' Extra ID / Memo: {order["info"]["extra_id_from"]}')
44+
print(f' Send exactly: {xmr_amount} XMR')
45+
print(f' Expected receive: ~{order["price"]} BTC -> {btc_address}')
46+
print(f' You have 15 minutes to send the deposit!\n')
47+
48+
input(' Press Enter once sent to start polling…\n')
49+
50+
# 3. Poll
51+
print('Polling for status (Ctrl+C to stop)…')
52+
terminal = {'closed', 'canceled'}
53+
poll_interval = 20
54+
55+
while True:
56+
status = exchange.fetch_order(order['id'])
57+
raw_status = status['info']['status']
58+
print(f' [status] {raw_status} (unified: {status["status"]})')
59+
60+
if status['status'] in terminal:
61+
if raw_status == 'finished':
62+
print(f'\nSwap finished!')
63+
print(f' BTC received: {status["info"]["amount_to"]}')
64+
print(f' Payout tx: {status["info"]["tx_to"]}')
65+
else:
66+
print(f'\nSwap ended with status: {raw_status}')
67+
break
68+
69+
time.sleep(poll_interval)
70+
71+
72+
if __name__ == '__main__':
73+
main()
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# WizardSwap — estimate a swap rate
2+
# No API key needed
3+
#
4+
# Usage: python examples/py/wizardswap-estimate-swap.py [BASE/QUOTE] [amount]
5+
# E.g.: python examples/py/wizardswap-estimate-swap.py XMR/BTC 1
6+
7+
import sys
8+
import ccxt
9+
10+
def main():
11+
symbol = sys.argv[1] if len(sys.argv) > 1 else 'XMR/BTC'
12+
amount_from = sys.argv[2] if len(sys.argv) > 2 else '1'
13+
14+
exchange = ccxt.wizardswap()
15+
16+
# Load markets (fetches currencies automatically)
17+
exchange.load_markets()
18+
19+
print(f'Estimating {amount_from} {symbol} swap on WizardSwap…')
20+
21+
ticker = exchange.fetch_ticker(symbol, {'amount_from': amount_from})
22+
23+
base, quote = symbol.split('/')
24+
print(f' Estimated receive: ~{ticker["last"]} {quote}')
25+
print(f' Fee: 2.2% (included in estimate)')
26+
print(f' Full response: {ticker["info"]}')
27+
28+
29+
if __name__ == '__main__':
30+
main()
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// WizardSwap — create a swap order and poll until finished
2+
// No API key needed
3+
//
4+
// ⚠ This example actually creates a real swap.
5+
// You must send the exact deposit amount within 15 minutes.
6+
//
7+
// Usage:
8+
// npx tsx examples/ts/wizardswap-create-swap.ts <BTC_ADDRESS> [XMR_AMOUNT]
9+
//
10+
// Example:
11+
// npx tsx examples/ts/wizardswap-create-swap.ts bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh 0.5
12+
13+
import ccxt from '../../ts/ccxt.js';
14+
15+
async function main () {
16+
const btcAddress = process.argv[2];
17+
const xmrAmount = parseFloat (process.argv[3] || '0.1');
18+
19+
if (!btcAddress) {
20+
console.error ('Usage: npx tsx examples/ts/wizardswap-create-swap.ts <BTC_ADDRESS> [XMR_AMOUNT]');
21+
process.exit (1);
22+
}
23+
24+
const exchange = new ccxt.wizardswap ();
25+
await exchange.loadMarkets ();
26+
27+
// ── 1. Estimate ──────────────────────────────────────────────────
28+
console.log (`Estimating ${xmrAmount} XMR → BTC …`);
29+
const ticker = await exchange.fetchTicker ('XMR/BTC', { 'amount_from': String (xmrAmount) });
30+
console.log (` Estimated receive: ~${ticker['last']} BTC (2.2 % fee included)\n`);
31+
32+
// ── 2. Create exchange ───────────────────────────────────────────
33+
console.log ('Creating swap order…');
34+
const order = await exchange.createOrder ('XMR/BTC', 'market', 'sell', xmrAmount, undefined, {
35+
'address_to': btcAddress,
36+
});
37+
38+
console.log (` Order ID: ${order['id']}`);
39+
console.log (` Deposit address: ${order['info']['address_from']}`);
40+
if (order['info']['extra_id_from']) {
41+
console.log (` Extra ID / Memo: ${order['info']['extra_id_from']}`);
42+
}
43+
console.log (` Send exactly: ${xmrAmount} XMR`);
44+
console.log (` Expected receive: ~${order['price']} BTC → ${btcAddress}`);
45+
console.log (` ⏱ You have 15 minutes to send the deposit!\n`);
46+
47+
// ── 3. Poll for status ───────────────────────────────────────────
48+
console.log ('Polling for status (Ctrl+C to stop)…');
49+
const terminal = new Set ([ 'closed', 'canceled' ]);
50+
const pollInterval = 20000; // 20 seconds
51+
52+
while (true) {
53+
const status = await exchange.fetchOrder (order['id']);
54+
const rawStatus = status['info']['status'];
55+
console.log (` [status] ${rawStatus} (unified: ${status['status']})`);
56+
57+
if (terminal.has (status['status'])) {
58+
if (rawStatus === 'finished') {
59+
console.log (`\n✅ Swap finished!`);
60+
console.log (` BTC received: ${status['info']['amount_to']}`);
61+
console.log (` Payout tx: ${status['info']['tx_to']}`);
62+
} else {
63+
console.log (`\n❌ Swap ended with status: ${rawStatus}`);
64+
}
65+
break;
66+
}
67+
68+
await new Promise ((resolve) => setTimeout (resolve, pollInterval));
69+
}
70+
}
71+
72+
main ().catch (console.error);
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// WizardSwap — estimate a swap rate
2+
// No API key needed
3+
//
4+
// Usage: npx tsx examples/ts/wizardswap-estimate-swap.ts [BASE/QUOTE] [amount]
5+
// E.g.: npx tsx examples/ts/wizardswap-estimate-swap.ts XMR/BTC 1
6+
7+
import ccxt from '../../ts/ccxt.js';
8+
9+
async function main () {
10+
const symbol = process.argv[2] || 'XMR/BTC';
11+
const amountFrom = process.argv[3] || '1';
12+
13+
const exchange = new ccxt.wizardswap ();
14+
15+
// WizardSwap has no predefined markets —
16+
// dynamically load currencies and build a market on the fly.
17+
await exchange.loadMarkets ();
18+
19+
console.log (`Estimating ${amountFrom} ${symbol} swap on WizardSwap…`);
20+
21+
const ticker = await exchange.fetchTicker (symbol, { 'amount_from': amountFrom });
22+
23+
const [ base, quote ] = symbol.split ('/');
24+
console.log (` Estimated receive: ~${ticker['last']} ${quote}`);
25+
console.log (` Fee: 2.2 % (included in estimate)`);
26+
console.log (` Full response:`, ticker['info']);
27+
}
28+
29+
main ().catch (console.error);
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// WizardSwap — fetch all supported currencies
2+
// No API key needed
3+
4+
import ccxt from '../../ts/ccxt.js';
5+
6+
async function main () {
7+
const exchange = new ccxt.wizardswap ();
8+
9+
console.log ('Fetching WizardSwap supported currencies…');
10+
const currencies = await exchange.fetchCurrencies ();
11+
12+
const codes = Object.keys (currencies);
13+
console.log (`Found ${codes.length} currencies:`);
14+
console.log (codes.join (', '));
15+
}
16+
17+
main ().catch (console.error);

ts/src/abstract/wizardswap.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// -------------------------------------------------------------------------------
2+
3+
// PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
4+
// https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
5+
6+
// -------------------------------------------------------------------------------
7+
8+
import { implicitReturnType } from '../base/types.js';
9+
import { Exchange as _Exchange } from '../base/Exchange.js';
10+
11+
interface Exchange {
12+
publicGetCurrencies (params?: {}): Promise<implicitReturnType>;
13+
publicGetExchangeId (params?: {}): Promise<implicitReturnType>;
14+
publicPostEstimate (params?: {}): Promise<implicitReturnType>;
15+
publicPostExchange (params?: {}): Promise<implicitReturnType>;
16+
}
17+
abstract class Exchange extends _Exchange {}
18+
19+
export default Exchange

0 commit comments

Comments
 (0)