@switch-win/sdk 1.2.9 → 1.3.0

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.
Files changed (73) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/GASLESS.md +407 -0
  3. package/LIMIT-ORDERS.md +213 -205
  4. package/README.md +131 -105
  5. package/ROBINHOOD.md +201 -198
  6. package/abi/GaslessSwitchV2ABI.json +1095 -0
  7. package/dist/constants.d.ts +276 -0
  8. package/dist/constants.d.ts.map +1 -0
  9. package/dist/constants.js +403 -0
  10. package/dist/constants.js.map +1 -0
  11. package/dist/gasless-client.d.ts +81 -0
  12. package/dist/gasless-client.d.ts.map +1 -0
  13. package/dist/gasless-client.js +467 -0
  14. package/dist/gasless-client.js.map +1 -0
  15. package/dist/gasless-index.d.ts +5 -0
  16. package/dist/gasless-index.d.ts.map +1 -0
  17. package/dist/gasless-index.js +5 -0
  18. package/dist/gasless-index.js.map +1 -0
  19. package/dist/gasless-operators.d.ts +32 -0
  20. package/dist/gasless-operators.d.ts.map +1 -0
  21. package/dist/gasless-operators.js +132 -0
  22. package/dist/gasless-operators.js.map +1 -0
  23. package/dist/gasless-store.d.ts +18 -0
  24. package/dist/gasless-store.d.ts.map +1 -0
  25. package/dist/gasless-store.js +83 -0
  26. package/dist/gasless-store.js.map +1 -0
  27. package/dist/gasless-types.d.ts +218 -0
  28. package/dist/gasless-types.d.ts.map +1 -0
  29. package/dist/gasless-types.js +2 -0
  30. package/dist/gasless-types.js.map +1 -0
  31. package/dist/gasless.d.ts +135 -0
  32. package/dist/gasless.d.ts.map +1 -0
  33. package/dist/gasless.js +513 -0
  34. package/dist/gasless.js.map +1 -0
  35. package/dist/index.d.ts +29 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +30 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/limit-orders.d.ts +440 -0
  40. package/dist/limit-orders.d.ts.map +1 -0
  41. package/dist/limit-orders.js +387 -0
  42. package/dist/limit-orders.js.map +1 -0
  43. package/dist/networks/index.d.ts +3 -0
  44. package/dist/networks/index.d.ts.map +1 -0
  45. package/dist/networks/index.js +2 -0
  46. package/dist/networks/index.js.map +1 -0
  47. package/dist/networks/robinhood.d.ts +536 -0
  48. package/dist/networks/robinhood.d.ts.map +1 -0
  49. package/dist/networks/robinhood.js +489 -0
  50. package/dist/networks/robinhood.js.map +1 -0
  51. package/dist/types.d.ts +436 -0
  52. package/dist/types.d.ts.map +1 -0
  53. package/dist/types.js +19 -0
  54. package/dist/types.js.map +1 -0
  55. package/examples/gasless-nextjs-proxy.ts +87 -0
  56. package/examples/gasless-operator.ts +76 -0
  57. package/examples/gasless-swap.ts +170 -0
  58. package/examples/limit-order-ethers.ts +195 -0
  59. package/examples/nextjs-proxy.ts +82 -0
  60. package/examples/react-hooks.tsx +396 -0
  61. package/examples/swap-ethers.ts +209 -0
  62. package/examples/swap-web3py.py +267 -0
  63. package/package.json +53 -12
  64. package/src/constants.ts +184 -182
  65. package/src/gasless-client.ts +823 -0
  66. package/src/gasless-index.ts +4 -0
  67. package/src/gasless-operators.ts +227 -0
  68. package/src/gasless-store.ts +137 -0
  69. package/src/gasless-types.ts +258 -0
  70. package/src/gasless.ts +756 -0
  71. package/src/index.ts +69 -63
  72. package/src/limit-orders.ts +206 -195
  73. package/src/types.ts +80 -55
@@ -0,0 +1,267 @@
1
+ """
2
+ Switch DEX Aggregator — Complete Swap Example (web3.py)
3
+
4
+ Demonstrates the full swap lifecycle:
5
+ 1. Fetch adapters (optional — for DEX filtering)
6
+ 2. Check token taxes (determines feeOnOutput mode)
7
+ 3. Get quote with exact expectedOutputAmount
8
+ 4. Approve ERC-20 (if needed)
9
+ 5. Send swap transaction
10
+
11
+ Usage:
12
+ pip install web3 requests
13
+ export SWITCH_API_KEY=your_key
14
+ export PRIVATE_KEY=your_private_key
15
+ python examples/swap-web3py.py
16
+
17
+ Prerequisites:
18
+ pip install web3 requests
19
+ """
20
+
21
+ import os
22
+ import sys
23
+ import requests
24
+ from web3 import Web3
25
+
26
+ # ── Constants --
27
+
28
+ API_BASE = "https://quote.switch.win"
29
+ SWITCH_ROUTER = "0x69033829f50244FD1be7BDC8e74aE0fF97E47126"
30
+ NATIVE_PLS = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"
31
+ WPLS = "0xA1077a294dDE1B09bB078844df40758a5D0f9a27"
32
+ RPC_URL = os.environ.get("RPC_URL", "https://rpc.pulsechain.com")
33
+
34
+ ERC20_ABI = [
35
+ {
36
+ "constant": True,
37
+ "inputs": [
38
+ {"name": "owner", "type": "address"},
39
+ {"name": "spender", "type": "address"},
40
+ ],
41
+ "name": "allowance",
42
+ "outputs": [{"name": "", "type": "uint256"}],
43
+ "type": "function",
44
+ },
45
+ {
46
+ "constant": False,
47
+ "inputs": [
48
+ {"name": "spender", "type": "address"},
49
+ {"name": "amount", "type": "uint256"},
50
+ ],
51
+ "name": "approve",
52
+ "outputs": [{"name": "", "type": "bool"}],
53
+ "type": "function",
54
+ },
55
+ ]
56
+
57
+ # ── Configuration --
58
+
59
+ API_KEY = os.environ.get("SWITCH_API_KEY")
60
+ PRIVATE_KEY = os.environ.get("PRIVATE_KEY")
61
+
62
+ if not API_KEY:
63
+ print("Error: Set SWITCH_API_KEY env var")
64
+ sys.exit(1)
65
+ if not PRIVATE_KEY:
66
+ print("Error: Set PRIVATE_KEY env var")
67
+ sys.exit(1)
68
+
69
+ # ── Swap parameters --
70
+
71
+ FROM_TOKEN = "0xA1077a294dDE1B09bB078844df40758a5D0f9a27" # WPLS
72
+ TO_TOKEN = "0x95B303987A60C71504D99Aa1b13B4DA07b0790ab" # PLSX
73
+ AMOUNT = 1000 * 10**18 # 1000 WPLS in wei
74
+ SLIPPAGE_BPS = 100 # 1%
75
+ FEE_BPS = 10 # 0.10%
76
+ PARTNER_ADDRESS = None # your partner wallet or None
77
+ RECEIVER_ADDRESS = None # custom recipient address or None to send to sender
78
+
79
+
80
+ # ── Helpers --
81
+
82
+ def api_fetch(path: str, params: dict | None = None) -> dict:
83
+ """Make an authenticated API call to Switch."""
84
+ resp = requests.get(
85
+ f"{API_BASE}{path}",
86
+ params=params,
87
+ headers={"x-api-key": API_KEY},
88
+ )
89
+ resp.raise_for_status()
90
+ return resp.json()
91
+
92
+
93
+ def determine_fee_on_output(
94
+ from_token: str,
95
+ to_token: str,
96
+ from_tax: dict,
97
+ to_tax: dict,
98
+ ) -> bool:
99
+ """Determine feeOnOutput based on tax info and PLS involvement.
100
+
101
+ - Selling a tax token → fee on output (avoid triggering sell-tax on fee)
102
+ - Buying a tax token → fee on input (avoid triggering buy-tax on fee)
103
+ - Buying PLS/WPLS → fee on output (collect PLS)
104
+ - Selling PLS/WPLS → fee on input (collect PLS)
105
+ - Default → False
106
+ """
107
+ pls_addresses = [NATIVE_PLS.lower(), WPLS.lower()]
108
+ from_addr = from_token.lower()
109
+ to_addr = to_token.lower()
110
+
111
+ if from_tax.get("isTaxToken") and from_tax.get("sellTaxBps", 0) > 0:
112
+ return True
113
+ if to_tax.get("isTaxToken") and to_tax.get("buyTaxBps", 0) > 0:
114
+ return False
115
+ if to_addr in pls_addresses:
116
+ return True
117
+ if from_addr in pls_addresses:
118
+ return False
119
+ return False
120
+
121
+
122
+ def swap(
123
+ w3: Web3,
124
+ account,
125
+ from_token: str,
126
+ to_token: str,
127
+ amount_wei: int,
128
+ slippage_bps: int = 50,
129
+ fee_bps: int = 0,
130
+ partner: str | None = None,
131
+ receiver: str | None = None,
132
+ ):
133
+ """Execute a full swap: adapters → checkTax → quote → approve → send."""
134
+ sender = account.address
135
+ print(f"Sender: {sender}")
136
+ print(f"Swapping {amount_wei} wei of {from_token} → {to_token}")
137
+
138
+ # ── Step 1: Fetch available adapters (optional — cache this) ─
139
+ print("\n── Step 1: Fetching adapters ──")
140
+ adapters_data = api_fetch("/swap/adapters")
141
+ adapter_names = [a["name"] for a in adapters_data["adapters"]]
142
+ print(f"Available DEXes: {', '.join(adapter_names)}")
143
+
144
+ # ── Step 2: Check token taxes --
145
+ print("\n── Step 2: Checking token taxes ──")
146
+ from_tax = api_fetch("/swap/checkTax", {"token": from_token, "network": "pulsechain"})
147
+ to_tax = api_fetch("/swap/checkTax", {"token": to_token, "network": "pulsechain"})
148
+
149
+ if from_tax.get("isTaxToken"):
150
+ print(f"From token: TAX (sell={from_tax['sellTaxBps']}bps, buy={from_tax['buyTaxBps']}bps)")
151
+ else:
152
+ print("From token: not a tax token")
153
+
154
+ if to_tax.get("isTaxToken"):
155
+ print(f"To token: TAX (sell={to_tax['sellTaxBps']}bps, buy={to_tax['buyTaxBps']}bps)")
156
+ else:
157
+ print("To token: not a tax token")
158
+
159
+ # ── Determine fee mode --
160
+ fee_on_output = determine_fee_on_output(from_token, to_token, from_tax, to_tax)
161
+ print(f"Fee mode: fee on {'output' if fee_on_output else 'input'}")
162
+
163
+ # ── Step 3: Fetch quote --
164
+ print("\n── Step 3: Fetching quote ──")
165
+ params = {
166
+ "network": "pulsechain",
167
+ "from": from_token,
168
+ "to": to_token,
169
+ "amount": str(amount_wei),
170
+ "sender": sender,
171
+ "slippage": str(slippage_bps),
172
+ "feeOnOutput": str(fee_on_output).lower(),
173
+ }
174
+ if fee_bps:
175
+ params["fee"] = str(fee_bps)
176
+ if partner:
177
+ params["partnerAddress"] = partner
178
+ if receiver:
179
+ params["receiver"] = receiver
180
+
181
+ quote = api_fetch("/swap/quote", params)
182
+
183
+ if "error" in quote:
184
+ raise Exception(f"Quote error: {quote['error']}")
185
+
186
+ tx_data = quote.get("tx")
187
+ tx_data_fee_on_output = quote.get("txFeeOnOutput")
188
+ if not tx_data:
189
+ raise Exception("No tx object in response — did you provide sender?")
190
+
191
+ # Choose the matching tx variant for the fee mode we determined
192
+ chosen_tx = tx_data_fee_on_output if fee_on_output and tx_data_fee_on_output else tx_data
193
+
194
+ print(f"Raw DEX output: {quote['totalAmountOut']}")
195
+ print(f"Expected received: {quote['expectedOutputAmount']}")
196
+ print(f"Min output: {quote['minAmountOut']}")
197
+ print(f"Eff. slippage: {quote['effectiveSlippagePercent']}%")
198
+ print(f"Paths: {len(quote['paths'])}")
199
+
200
+ # ── Step 4: Approve ERC-20 if needed --
201
+ if from_token.lower() != NATIVE_PLS.lower():
202
+ token = w3.eth.contract(
203
+ address=Web3.to_checksum_address(from_token), abi=ERC20_ABI
204
+ )
205
+ current_allowance = token.functions.allowance(
206
+ sender, chosen_tx["to"]
207
+ ).call()
208
+
209
+ if current_allowance < amount_wei:
210
+ print("\nApproving SwitchRouter to spend tokens...")
211
+ approve_tx = token.functions.approve(
212
+ chosen_tx["to"], amount_wei
213
+ ).build_transaction(
214
+ {
215
+ "from": sender,
216
+ "nonce": w3.eth.get_transaction_count(sender),
217
+ }
218
+ )
219
+ signed = account.sign_transaction(approve_tx)
220
+ tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)
221
+ receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
222
+ print(f"Approved in tx: {receipt['transactionHash'].hex()}")
223
+ else:
224
+ print("\nSufficient allowance already granted")
225
+
226
+ # ── Step 5: Send swap --
227
+ print("\nSending swap transaction...")
228
+ swap_tx = {
229
+ "from": sender,
230
+ "to": Web3.to_checksum_address(chosen_tx["to"]),
231
+ "data": chosen_tx["data"],
232
+ "value": int(chosen_tx["value"]),
233
+ "nonce": w3.eth.get_transaction_count(sender),
234
+ }
235
+
236
+ signed = account.sign_transaction(swap_tx)
237
+ tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)
238
+ print(f"Tx hash: {tx_hash.hex()}")
239
+
240
+ receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
241
+ print(f"\nSwap confirmed in block {receipt['blockNumber']}")
242
+ return receipt
243
+
244
+
245
+ def main():
246
+ w3 = Web3(Web3.HTTPProvider(RPC_URL))
247
+ if not w3.is_connected():
248
+ print("Error: Could not connect to RPC")
249
+ sys.exit(1)
250
+
251
+ account = w3.eth.account.from_key(PRIVATE_KEY)
252
+
253
+ swap(
254
+ w3=w3,
255
+ account=account,
256
+ from_token=FROM_TOKEN,
257
+ to_token=TO_TOKEN,
258
+ amount_wei=AMOUNT,
259
+ slippage_bps=SLIPPAGE_BPS,
260
+ fee_bps=FEE_BPS,
261
+ partner=PARTNER_ADDRESS,
262
+ receiver=RECEIVER_ADDRESS,
263
+ )
264
+
265
+
266
+ if __name__ == "__main__":
267
+ main()
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@switch-win/sdk",
3
- "version": "1.2.9",
4
- "description": "Official integration kit for the Switch DEX Aggregator on PulseChain and Robinhood Chain",
3
+ "version": "1.3.0",
4
+ "description": "Switch swaps, limit orders, and GaslessSwitch V2 integrations on PulseChain and Robinhood Chain",
5
5
  "author": "BuildTheTech",
6
6
  "license": "MIT",
7
7
  "repository": {
@@ -21,26 +21,67 @@
21
21
  "sdk"
22
22
  ],
23
23
  "type": "module",
24
- "main": "src/index.js",
25
- "types": "src/index.d.ts",
24
+ "main": "dist/index.js",
25
+ "types": "dist/index.d.ts",
26
26
  "exports": {
27
- ".": "./src/index.ts",
28
- "./types": "./src/types.ts",
29
- "./constants": "./src/constants.ts",
30
- "./networks": "./src/networks/index.ts",
31
- "./networks/robinhood": "./src/networks/robinhood.ts",
32
- "./limit-orders": "./src/limit-orders.ts",
27
+ ".": {
28
+ "types": "./dist/index.d.ts",
29
+ "default": "./dist/index.js"
30
+ },
31
+ "./types": {
32
+ "types": "./dist/types.d.ts",
33
+ "default": "./dist/types.js"
34
+ },
35
+ "./constants": {
36
+ "types": "./dist/constants.d.ts",
37
+ "default": "./dist/constants.js"
38
+ },
39
+ "./networks": {
40
+ "types": "./dist/networks/index.d.ts",
41
+ "default": "./dist/networks/index.js"
42
+ },
43
+ "./networks/robinhood": {
44
+ "types": "./dist/networks/robinhood.d.ts",
45
+ "default": "./dist/networks/robinhood.js"
46
+ },
47
+ "./limit-orders": {
48
+ "types": "./dist/limit-orders.d.ts",
49
+ "default": "./dist/limit-orders.js"
50
+ },
51
+ "./gasless": {
52
+ "types": "./dist/gasless-index.d.ts",
53
+ "default": "./dist/gasless-index.js"
54
+ },
55
+ "./gasless/operators": {
56
+ "types": "./dist/gasless-operators.d.ts",
57
+ "default": "./dist/gasless-operators.js"
58
+ },
33
59
  "./abi": "./abi/SwitchRouterABI.json",
34
- "./abi/limit-order": "./abi/SwitchLimitOrderABI.json"
60
+ "./abi/limit-order": "./abi/SwitchLimitOrderABI.json",
61
+ "./abi/gasless": "./abi/GaslessSwitchV2ABI.json"
35
62
  },
36
63
  "files": [
64
+ "dist/",
37
65
  "src/",
38
66
  "abi/",
67
+ "examples/",
68
+ "CHANGELOG.md",
39
69
  "README.md",
40
70
  "ROBINHOOD.md",
41
- "LIMIT-ORDERS.md"
71
+ "LIMIT-ORDERS.md",
72
+ "GASLESS.md"
42
73
  ],
74
+ "scripts": {
75
+ "build": "tsc -p tsconfig.json",
76
+ "typecheck": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.examples.json --noEmit",
77
+ "test": "npm run build && node --test test/*.test.mjs",
78
+ "check:gasless-live": "npm run build && node scripts/check-gasless-live.mjs",
79
+ "prepack": "npm run build"
80
+ },
43
81
  "devDependencies": {
44
82
  "typescript": "^5.9.3"
83
+ },
84
+ "dependencies": {
85
+ "ethers": "6.17.0"
45
86
  }
46
87
  }