Official integration kit for the Switch DEX Aggregator on PulseChain
Everything partners need to integrate Switch swaps — API docs, TypeScript types, ABIs, constants, and ready-to-use examples.
Base URL: https://quote.switch.win | Chain: PulseChain (369)
Switch-SDK/
├── README.md # This file — full API integration guide
├── src/
│ ├── types.ts # TypeScript response types
│ └── constants.ts # Addresses, ABIs, and config constants
├── abi/
│ └── SwitchRouterABI.json # Full SwitchRouter contract ABI
├── examples/
│ ├── swap-ethers.ts # Complete swap example (ethers.js v6)
│ ├── swap-web3py.py # Complete swap example (web3.py)
│ └── nextjs-proxy.ts # Next.js API route proxy for key security
└── package.json
- Quickstart
- Authentication
- Get Swap Quote
- Executing a Swap
- Important Notes
- Response Schema
- Tax Tokens (Fee-on-Transfer)
- Error Handling
- Partner Fee Sharing
- Constants & Addresses
- Full Integration Examples
- Rate Limits
- Support
Get a swap quote and execute it in three steps:
# 1. Get quote with tx calldata
curl -H "x-api-key: YOUR_KEY" \
"https://quote.switch.win/swap/quote?network=pulsechain&from=0xA1077a294dDE1B09bB078844df40758a5D0f9a27&to=0x95B303987A60C71504D99Aa1b13B4DA07b0790ab&amount=1000000000000000000&sender=0xYOUR_WALLET&slippage=100"
# 2. Approve the SwitchRouter to spend your input token (ERC-20 only, skip for native PLS)
# 3. Send the transaction using the `tx` object from the response:
# { to: "0x33A6...", data: "0x...", value: "0" }The response tx field contains a fully-encoded goSwitch() call — just forward it to the chain. See Executing a Swap for details.
import type { BestPathResponse, SwapTransaction } from "@switch-win/sdk/types";
import { SWITCH_ROUTER, NATIVE_PLS, API_BASE } from "@switch-win/sdk/constants";
const res = await fetch(`${API_BASE}/swap/quote?network=pulsechain&from=${NATIVE_PLS}&to=0x95B3...&amount=1000000000000000000&sender=${wallet}`, {
headers: { "x-api-key": process.env.SWITCH_API_KEY! },
});
const quote: BestPathResponse = await res.json();
if (quote.tx) {
await signer.sendTransaction(quote.tx);
}Every request must include an API key via one of:
| Method | Header | Value |
|---|---|---|
| Custom header | x-api-key |
<your-api-key> |
| Bearer token | Authorization |
Bearer <your-api-key> |
Contact the Switch team to obtain an API key.
Example:
curl -H "x-api-key: YOUR_KEY" "https://quote.switch.win/swap/quote?network=pulsechain&from=0xA1077a294dDE1B09bB078844df40758a5D0f9a27&to=0x95B303987A60C71504D99Aa1b13B4DA07b0790ab&amount=1000000000000000000&sender=0xYourWallet"Your API key is a secret credential — never expose it in client-side code (browser JavaScript, mobile app bundles, public repositories, etc.). Anyone who obtains your key can make requests on your behalf and consume your rate limit.
Recommended architecture: Route all Switch API calls through your own backend server, which holds the key privately and proxies requests to Switch.
┌──────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Browser │──────▶│ Your Backend │──────▶│ Switch API │
│ / App │ (no │ (holds key) │ x-api- │ quote.switch.win│
│ │ key) │ │ key │ │
└──────────┘ └─────────────────┘ └─────────────────┘
Best practices:
| Do | Don't |
|---|---|
Store the key in server-side environment variables (process.env, .env files excluded from git) |
Embed the key in frontend JavaScript, HTML, or mobile app code |
Use a backend proxy endpoint (e.g. /api/quote) that attaches the key before forwarding to Switch |
Commit .env files or key values to version control |
| Restrict your backend proxy with CORS to only your own domain(s) | Log or print API keys in production |
| Rotate your key immediately if you suspect it has been leaked | Share a single key across unrelated projects |
See
examples/nextjs-proxy.tsfor a ready-to-use Next.js API route that keeps your key server-side.
GET /swap/quote
Returns the optimal split-route for a swap and (optionally) a ready-to-send transaction object.
| Parameter | Required | Type | Default | Description |
|---|---|---|---|---|
network |
Yes | string | — | Target blockchain network. Currently only "pulsechain" is supported. |
from |
Yes | address | — | Input token address. Use 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE for native PLS. |
to |
Yes | address | — | Output token address. Use 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE for native PLS. |
amount |
Yes | string | — | Input amount in wei (raw integer string, no decimals). Max: 10²⁷. |
sender |
No* | address | — | Sender wallet address. Required to receive tx calldata in the response. |
receiver |
No | address | sender |
Custom recipient address. If omitted, output tokens are sent to sender. |
slippage |
No | integer | 50 |
Slippage tolerance in basis points (bps). 50 = 0.50 %. Range: 0–5000. |
fee |
No | integer | 25 |
Protocol fee in basis points (0.25 %). Range: 25–100. Defaults to 25 if omitted. |
partnerAddress |
No | address | 0x0…0 |
Your partner wallet to receive 50 % of collected fees. Omit or pass 0x0 for no partner. |
* If
senderis omitted, the response will still contain routing data,minAmountOut, and tax info, but thetxobject will be absent. This is useful for showing estimated swap output before the user connects their wallet — you can display prices, routes, and tax warnings without requiring a wallet connection. Once the user connects, re-fetch the quote withsenderto get the ready-to-sendtxobject.
GET /swap/quote?network=pulsechain&from=0xA1077a294dDE1B09bB078844df40758a5D0f9a27&to=0x95B303987A60C71504D99Aa1b13B4DA07b0790ab&amount=1000000000000000000&sender=0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045&slippage=100&fee=30&partnerAddress=0xYourPartnerWallet
Call GET /swap/quote with all desired parameters including sender.
If the input token is an ERC-20 (not native PLS), the user must approve the SwitchRouter contract to spend amount tokens before submitting the swap.
import { SWITCH_ROUTER, ERC20_ABI } from "@switch-win/sdk/constants";
const tokenContract = new ethers.Contract(fromTokenAddress, ERC20_ABI, signer);
const approveTx = await tokenContract.approve(SWITCH_ROUTER, amount);
await approveTx.wait();Tip: You can skip this step if the user has already granted a sufficient allowance.
If the input is native PLS, no approval is needed — the value is sent with the transaction.
Use the tx object from the quote response directly:
const { tx } = quoteResponse;
const txResponse = await signer.sendTransaction({
to: tx.to,
data: tx.data,
value: tx.value, // "0" for ERC-20 inputs, amountIn for native PLS
});
const receipt = await txResponse.wait();
console.log("Swap confirmed:", receipt.hash);That's it. The tx.data already encodes the correct goSwitch() call with your routes, slippage protection, fee, and partner address.
See
examples/swap-ethers.tsfor a complete working example.
Some tokens on PulseChain charge a percentage fee on every transfer() call — commonly called tax tokens or fee-on-transfer tokens. When you swap into or out of one of these tokens, the actual amount received differs from the quoted DEX output because the token's own contract skims a fee during the transfer.
The API automatically detects tax tokens via empirical simulation and returns the tax rates in the response:
{
// Selling a token with 1.2% sell tax
"fromTokenTax": { "isTaxToken": true, "buyTaxBps": 100, "sellTaxBps": 120 },
// Buying a non-tax token
"toTokenTax": { "isTaxToken": false, "buyTaxBps": 0, "sellTaxBps": 0 },
// minAmountOut already accounts for the 1.2% sell tax + user slippage
"minAmountOut": "...",
// Effective slippage = user slippage (0.5%) + sell tax (1.2%) = 1.7%
"effectiveSlippageBps": 170,
"effectiveSlippagePercent": "1.7"
}How it works:
- If
fromTokenis a tax token, itssellTaxBpsreduces the effective input reaching the DEX pools. This is the relevant tax when the token is being sold (sent out of the user's wallet). - If
toTokenis a tax token, itsbuyTaxBpsreduces what the user ultimately receives. This is the relevant tax when the token is being bought (received into the user's wallet). - Both taxes (if applicable) plus the user's slippage tolerance are factored into
minAmountOut, so the transaction won't revert unexpectedly.
Key fields for integrators:
| Field | Description |
|---|---|
fromTokenTax.sellTaxBps |
Sell tax applied when fromToken is the input (e.g. 120 = 1.2%) |
toTokenTax.buyTaxBps |
Buy tax applied when toToken is the output (e.g. 500 = 5%) |
effectiveSlippageBps |
User slippage + sell tax + buy tax in basis points |
effectiveSlippagePercent |
Same as above, as a human-readable % string (e.g. "1.7") |
minAmountOut |
Minimum output with taxes + fee + slippage already applied — use this directly |
UI recommendations:
- Display a warning when
fromTokenTax.isTaxTokenortoTokenTax.isTaxTokenistrue. - Show the tax percentage to the user (e.g. "1.2% sell tax") so they understand the impact.
- Display
effectiveSlippagePercentas the total slippage indicator — e.g. "Slippage: 1.7% (includes 1.2% sell tax)". - Use
minAmountOutdirectly from the API response — it already includes taxes, fees, and slippage. Do not re-compute it client-side.
// Example: display effective slippage in your UI
const label = `Slippage: ${quote.effectiveSlippagePercent}%`;
const fromIsTax = quote.fromTokenTax?.isTaxToken ?? false;
const toIsTax = quote.toTokenTax?.isTaxToken ?? false;
if (fromIsTax) {
// Show: "Slippage: 1.7% (includes 1.2% sell tax)"
label += ` (includes ${quote.fromTokenTax!.sellTaxBps / 100}% sell tax)`;
}
if (toIsTax) {
// Show: "Slippage: 5.5% (includes 5% buy tax)"
label += ` (includes ${quote.toTokenTax!.buyTaxBps / 100}% buy tax)`;
}Quotes reflect current on-chain liquidity and are cached for ~10 seconds. DEX prices can shift between when you fetch a quote and when the transaction lands on-chain — this is what minAmountOut (slippage protection) guards against.
Best practice: Fetch a fresh quote immediately before sending the transaction. Do not cache quotes for more than a few seconds on your side.
All amounts (amount, totalAmountIn, totalAmountOut, minAmountOut, etc.) are raw integer strings in the token's smallest unit (wei). To convert a human-readable amount to wei, multiply by 10^decimals:
| Token | Decimals | 1.0 token in wei |
|---|---|---|
| PLS / WPLS | 18 | "1000000000000000000" |
| USDC (bridged) | 6 | "1000000" |
| USDT (bridged) | 6 | "1000000" |
// ethers.js v6
const amountWei = ethers.parseUnits("1.5", 18).toString(); // "1500000000000000000"- Use
0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE(the native sentinel address) to swap native PLS. - The router handles wrapping/unwrapping automatically:
- Selling native PLS: Send PLS as
msg.value— the router wraps it to WPLS internally. - Buying native PLS: The router unwraps WPLS and sends native PLS to the recipient.
- Selling native PLS: Send PLS as
- If you want to swap WPLS the ERC-20 token directly (without wrapping/unwrapping), use the WPLS address
0xA1077a294dDE1B09bB078844df40758a5D0f9a27.
The tx object does not include a gas field. We recommend either:
- Letting your wallet estimate gas — most wallet libraries (ethers.js, web3.py, MetaMask) auto-estimate when
gasis omitted. - Using a generous manual limit —
500,000–1,000,000gas covers most multi-hop split swaps on PulseChain. Complex routes with many hops/legs may need more.
const txResponse = await signer.sendTransaction({
...quote.tx,
gasLimit: 800_000, // optional: override if auto-estimate is unreliable
});Before sending an approval transaction, check whether the user already has sufficient allowance to avoid wasting gas on a redundant approve:
import { SWITCH_ROUTER, ERC20_ABI } from "@switch-win/sdk/constants";
const token = new ethers.Contract(fromToken, ERC20_ABI, signer);
const currentAllowance = await token.allowance(sender, SWITCH_ROUTER);
if (currentAllowance < BigInt(amount)) {
const approveTx = await token.approve(SWITCH_ROUTER, amount);
await approveTx.wait();
}If the transaction reverts, the SwitchRouter contract returns one of these custom errors:
| Error | Meaning |
|---|---|
FinalAmountOutTooLow() |
Output after fees fell below _minTotalAmountOut — price moved beyond your slippage tolerance. Retry with a fresh quote or increase slippage. |
ExcessiveFee() |
_fee exceeds the contract maximum (100 bps / 1 %). |
InsufficientFee() |
_fee is below the protocol's MIN_FEE. Contact the Switch team if you need a lower fee. |
MsgValueMismatch() |
For native PLS swaps, msg.value must exactly equal the route's total amountIn. |
ZeroInput() |
No input amount was provided. |
TypeScript interfaces for all types below are available in
src/types.ts.
| Field | Type | Description |
|---|---|---|
fromToken |
string |
Input token address |
toToken |
string |
Output token address |
receiver |
string? |
Recipient address for output tokens. Same as sender unless a custom receiver was specified. |
totalAmountIn |
string |
Total input amount in wei |
totalAmountOut |
string |
Raw DEX output in wei — reflects price impact only (before taxes, fees, and slippage) |
minAmountOut |
string |
Minimum acceptable output after token taxes, fee, and slippage. When tax tokens are involved: totalAmountOut × (10000 − sellTaxBps) / 10000 × (10000 − feeBps) / 10000 × (10000 − buyTaxBps) / 10000 × (10000 − slippageBps) / 10000. This is the value encoded in the tx calldata as _minTotalAmountOut. |
paths |
SwapPath[] |
Human-readable path descriptions |
routeAllocation |
RouteAllocationPlan |
Structured route breakdown (matches on-chain structs) |
tx |
SwapTransaction? |
Ready-to-send transaction (fee on input). Only present when sender is provided. |
txFeeOnOutput |
SwapTransaction? |
Ready-to-send transaction (fee on output). Only present when sender is provided. Choose between tx and txFeeOnOutput at send time. |
fromTokenTax |
TokenTaxResponse? |
Transfer tax info for the input token. See Tax Tokens. |
toTokenTax |
TokenTaxResponse? |
Transfer tax info for the output token. See Tax Tokens. |
effectiveSlippageBps |
number |
User slippage + fromTokenTax.sellTaxBps + toTokenTax.buyTaxBps combined in basis points. |
effectiveSlippagePercent |
string |
Same as effectiveSlippageBps as a human-readable percentage (e.g. "1.7"). |
A human-readable summary of each route split. Useful for display purposes (e.g. showing the user "60% via PulseXV2, 40% via UniswapV3").
| Field | Type | Description |
|---|---|---|
adapter |
string |
Human-readable DEX adapter name (e.g. "PulseXV2", "UniswapV3") |
amountIn |
string |
Input amount for this path (wei) |
amountOut |
string |
Expected output for this path (wei) |
path |
string[] |
Ordered list of token addresses traversed (e.g. [tokenIn, intermediate, tokenOut]) |
adapters |
string[] |
Human-readable adapter names used at each hop |
percentage |
number? |
Percentage of total input routed through this path |
legs |
SwapPathLeg[]? |
Detailed breakdown of each hop-leg in this path |
| Field | Type | Description |
|---|---|---|
tokenIn |
string |
Leg input token |
tokenOut |
string |
Leg output token |
adapter |
string? |
Human-readable DEX adapter name |
amountIn |
string |
Input amount (wei) |
amountOut |
string |
Output amount (wei) |
percentage |
number? |
Percentage of the hop routed through this adapter |
| Field | Type | Description |
|---|---|---|
to |
string |
SwitchRouter contract address |
data |
string |
ABI-encoded goSwitch() calldata |
value |
string |
Native PLS to send (wei). "0" for ERC-20 input tokens. |
Present on every quote response as fromTokenTax and toTokenTax. Reports whether each token charges a fee on transfer ("tax token").
| Field | Type | Description |
|---|---|---|
isTaxToken |
boolean |
true if the token has a non-zero transfer tax |
buyTaxBps |
number |
Buy tax in basis points — applied when the token is acquired (i.e. it is the output token). 500 = 5 %. |
sellTaxBps |
number |
Sell tax in basis points — applied when the token is sold (i.e. it is the input token). 500 = 5 %. |
When a tax token is involved,
minAmountOutalready accounts for the tax so the on-chain slippage check passes correctly.
| Field | Type | Description |
|---|---|---|
amountIn |
string |
Total input in wei |
totalAmountOut |
string |
Total expected output in wei |
routes |
SingleRouteAllocation[] |
Array of split routes |
| Field | Type | Description |
|---|---|---|
amountIn |
string |
Input amount for this route portion |
hops |
HopAllocationPlan[] |
Sequential hops in this route |
| Field | Type | Description |
|---|---|---|
tokenIn |
string |
Hop input token |
tokenOut |
string |
Hop output token |
totalAmountOut |
string |
Total output from all legs in this hop |
legs |
HopAdapterAllocationPlan[] |
Adapter legs executing this hop (can be split across DEXes) |
| Field | Type | Description |
|---|---|---|
adapter |
string |
Human-readable DEX adapter name (e.g. "PulseXV2") |
amountIn |
string |
Input amount routed through this adapter |
amountOut |
string |
Expected output from this adapter |
Errors are returned as JSON with an error field:
{ "error": "Missing required parameters: from, to, amount" }| HTTP Status | Meaning |
|---|---|
200 |
Successful quote |
400 |
Invalid parameters (missing, malformed, out of range) |
401 |
Missing API key |
403 |
Invalid API key |
429 |
Rate limit exceeded (per-key total or per-IP sub-limit) |
502 |
Backend routing failure |
| Error | Cause |
|---|---|
"Missing required parameter: network" |
network query param absent |
"This network is not supported at this time." |
network is not "pulsechain" |
"Missing required parameters: from, to, amount" |
One or more required query params absent |
"Invalid from address (must be 0x + 40 hex chars)" |
from is not a valid hex address |
"Invalid to address (must be 0x + 40 hex chars)" |
to is not a valid hex address |
"Cannot swap token to itself" |
from and to are the same address |
"Amount must be a valid integer string" |
amount is not a parseable integer |
"Amount must be greater than 0" |
amount is zero or negative |
"Amount exceeds maximum allowed value" |
amount exceeds 10²⁷ (1 billion tokens @ 18 decimals) |
"Slippage must be a number between 0 and 5000 (basis points)" |
slippage out of range |
"Fee must be a number between 25 and 100 (basis points). Minimum fee is 0.25%." |
fee out of range or below minimum |
"receiver must be a valid Ethereum address (0x + 40 hex chars)" |
Malformed receiver address |
"partnerAddress must be a valid Ethereum address (0x + 40 hex chars)" |
Malformed partner address |
"Failed to find route" |
No viable swap path exists, or route computation timed out (30s limit) |
Switch supports a 50/50 fee-sharing model for integration partners:
- Set a
fee(e.g.30= 0.30 %, minimum25= 0.25 %) and yourpartnerAddresswhen calling the API. - The SwitchRouter contract automatically splits collected fees during the swap:
- 50 % sent to the protocol
- 50 % sent directly to your
partnerAddress
No claiming step is required — your share arrives in the same transaction as the swap.
If
partnerAddressis omitted or0x0, the fee accrues entirely to the protocol.
Every quote response includes two transaction objects (when sender is provided):
| Field | feeOnOutput |
How it works | User experience |
|---|---|---|---|
tx (default) |
false |
Fee is deducted from the input amount before routing. The user sends the full amount but less gets routed through DEX pools. |
User sends exactly amount tokens. Output is slightly lower because less was routed. |
txFeeOnOutput |
true |
Full input is routed through DEX pools, then the fee is deducted from the output before delivery. | User sends exactly amount tokens. The gross output is higher but the fee is taken from it. |
Both modes produce similar net results. Fee on input (tx) is the default and most common choice. Fee on output (txFeeOnOutput) can be preferable when you want to collect fees in the output token.
Since every response contains both variants, you can decide which to use at send time — no need to re-fetch the quote.
Note: The
minAmountOutin the API response already accounts for the fee in both modes, so the on-chain slippage check will pass correctly regardless of which variant you send.
The best fee mode depends on your use case. Here are battle-tested strategies for choosing between tx and txFeeOnOutput:
If one side of the swap is PLS or WPLS, take the fee on that side so your collected fees accumulate in the chain's native token — easy to use for gas, payroll, or liquidity.
import { NATIVE_PLS, WPLS } from "@switch-win/sdk/constants";
function shouldFeeOnOutput(fromToken: string, toToken: string): boolean {
const plsAddresses = [NATIVE_PLS.toLowerCase(), WPLS.toLowerCase()];
// Selling PLS → fee on input (collect PLS)
if (plsAddresses.includes(fromToken.toLowerCase())) return false;
// Buying PLS → fee on output (collect PLS)
if (plsAddresses.includes(toToken.toLowerCase())) return true;
// Neither side is PLS — default
return false;
}Accumulate fees in high-liquidity, stable-value tokens (DAI, USDC, USDT, HEX, etc.) to reduce your exposure to small-cap volatility.
import { BLUE_CHIPS } from "@switch-win/sdk/constants";
function shouldFeeOnOutput(fromToken: string, toToken: string): boolean {
const fromIsBlue = BLUE_CHIPS.has(fromToken.toLowerCase());
const toIsBlue = BLUE_CHIPS.has(toToken.toLowerCase());
if (toIsBlue && !fromIsBlue) return true;
if (fromIsBlue && !toIsBlue) return false;
return false;
}If you run a project and one side of the swap is YOUR token, take the fee in the other token. This avoids accumulating your own token (which you can mint/earn anyway) and instead collects diversified value.
const MY_PROJECT_TOKEN = "0xYourProjectTokenAddress".toLowerCase();
function shouldFeeOnOutput(fromToken: string, toToken: string): boolean {
// Selling your token → fee on output (collect the other token)
if (fromToken.toLowerCase() === MY_PROJECT_TOKEN) return true;
// Buying your token → fee on input (collect the other token)
if (toToken.toLowerCase() === MY_PROJECT_TOKEN) return false;
return false;
}If one side of the swap is a tax token, take the fee from the other side. Collecting a tax token as fee means the transfer tax eats into your fee revenue.
import type { BestPathResponse } from "@switch-win/sdk/types";
function shouldFeeOnOutput(quote: BestPathResponse): boolean {
const fromIsTax = quote.fromTokenTax?.isTaxToken ?? false;
const toIsTax = quote.toTokenTax?.isTaxToken ?? false;
// Selling a tax token → fee on output (collect the non-tax output token)
if (fromIsTax && !toIsTax) return true;
// Buying a tax token → fee on input (collect the non-tax input token)
if (toIsTax && !fromIsTax) return false;
// Both or neither are tax tokens — fall through to other heuristics
return false;
}Note: This strategy requires the quote response (to read
fromTokenTax/toTokenTax), which you already have since bothtxobjects arrive in the same response — no second fetch needed.
Combine the above in priority order for maximum flexibility:
import { NATIVE_PLS, WPLS, BLUE_CHIPS } from "@switch-win/sdk/constants";
import type { BestPathResponse } from "@switch-win/sdk/types";
const MY_PROJECT_TOKEN = "0xYourProjectTokenAddress".toLowerCase();
const plsAddresses = [NATIVE_PLS.toLowerCase(), WPLS.toLowerCase()];
function shouldFeeOnOutput(from: string, to: string, quote?: BestPathResponse): boolean {
const fromAddr = from.toLowerCase();
const toAddr = to.toLowerCase();
// Priority 1: If either token is your project token, take fee in the opposite
if (fromAddr === MY_PROJECT_TOKEN) return true; // fee on output = collect output
if (toAddr === MY_PROJECT_TOKEN) return false; // fee on input = collect input
// Priority 2: Avoid collecting tax tokens (fee revenue lost to transfer tax)
if (quote) {
const fromIsTax = quote.fromTokenTax?.isTaxToken ?? false;
const toIsTax = quote.toTokenTax?.isTaxToken ?? false;
if (fromIsTax && !toIsTax) return true; // collect non-tax output
if (toIsTax && !fromIsTax) return false; // collect non-tax input
}
// Priority 3: Prefer collecting PLS/WPLS
if (plsAddresses.includes(fromAddr)) return false; // fee on input = collect PLS
if (plsAddresses.includes(toAddr)) return true; // fee on output = collect PLS
// Priority 4: Prefer collecting blue chips
if (BLUE_CHIPS.has(toAddr) && !BLUE_CHIPS.has(fromAddr)) return true;
if (BLUE_CHIPS.has(fromAddr) && !BLUE_CHIPS.has(toAddr)) return false;
// Default: fee on input
return false;
}Then send the chosen transaction:
const useFeeOnOutput = shouldFeeOnOutput(from, to, quote);
const chosenTx = useFeeOnOutput ? quote.txFeeOnOutput! : quote.tx!;
await signer.sendTransaction(chosenTx);Tip: You can choose differently on every swap — adapt dynamically based on the token pair.
All constants are importable from src/constants.ts.
| Name | Value |
|---|---|
| Chain | PulseChain (Chain ID 369) |
| SwitchRouter | 0x69033829f50244FD1be7BDC8e74aE0fF97E47126 |
| Native PLS sentinel | 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE |
| WPLS | 0xA1077a294dDE1B09bB078844df40758a5D0f9a27 |
| Fee denominator | 10000 (basis points) |
| Max slippage | 5000 bps (50 %) |
| Max fee | 100 bps (1 %) |
| Min fee | 25 bps (0.25 %) — enforced on-chain by MIN_FEE |
| Default slippage | 50 bps (0.50 %) |
Complete runnable examples are in the examples/ directory:
| File | Language | Description |
|---|---|---|
swap-ethers.ts |
TypeScript | Full swap flow with ethers.js v6 (quote → approve → send) |
swap-web3py.py |
Python | Full swap flow with web3.py |
nextjs-proxy.ts |
TypeScript | Next.js API route proxy to keep your API key server-side |
All requests require a valid API key. The API enforces dual-bucket rate limiting — both checks must pass:
| Bucket | Scope | Default Limit | Description |
|---|---|---|---|
| Per-key | All IPs sharing one API key | 100 req/min | Total throughput for the key (configurable per key) |
| Per-IP | Each unique IP within a key | 10 req/min | Prevents one caller from monopolising a shared key |
Both limits apply simultaneously. A single IP can never exceed 10/min, and all IPs combined can never push a key past its total limit.
Every successful response includes informational headers:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 87
If your backend proxies Switch quotes to end users (server-to-server calls), ask the Switch team to whitelist your server IP(s) for your API key. Whitelisted IPs skip the 10/min per-IP sub-limit but still count toward the key's total — giving your backend access to the key's full allocation.
If you receive a 429 Too Many Requests response, back off and retry after a short delay using exponential backoff.
To request a new API key, IP whitelisting, or rate limit changes, contact the Switch team (see Support).
For API key requests, integration help, or feature requests, contact the Switch team:
- Website: switch.win
- Telegram:
MIT
Last updated: February 2026
{ "fromToken": "0xA1077a294dDE1B09bB078844df40758a5D0f9a27", "toToken": "0x95B303987A60C71504D99Aa1b13B4DA07b0790ab", "receiver": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", "totalAmountIn": "1000000000000000000", "totalAmountOut": "52934810000000000000", "minAmountOut": "52405461900000000000", // totalAmountOut adjusted for tax + fee + slippage "fromTokenTax": { "isTaxToken": false, "buyTaxBps": 0, "sellTaxBps": 0 }, "toTokenTax": { "isTaxToken": false, "buyTaxBps": 0, "sellTaxBps": 0 }, "paths": [ /* ... detailed path info ... */ ], "routeAllocation": { "amountIn": "1000000000000000000", "totalAmountOut": "52934810000000000000", "routes": [ { "amountIn": "600000000000000000", "hops": [ { "tokenIn": "0xA1077a294dDE1B09bB078844df40758a5D0f9a27", "tokenOut": "0x95B303987A60C71504D99Aa1b13B4DA07b0790ab", "totalAmountOut": "31893000000000000000", "legs": [ { "adapter": "PulseXV2", "amountIn": "600000000000000000", "amountOut": "31893000000000000000" } ] } ] }, { "amountIn": "400000000000000000", "hops": [ { "tokenIn": "0xA1077a294dDE1B09bB078844df40758a5D0f9a27", "tokenOut": "0x57fde0a71132198bbec939b98976993d8d89d225", "totalAmountOut": "2100000000", "legs": [ { "adapter": "UniswapV3", "amountIn": "400000000000000000", "amountOut": "2100000000" } ] }, { "tokenIn": "0x57fde0a71132198bbec939b98976993d8d89d225", "tokenOut": "0x95B303987A60C71504D99Aa1b13B4DA07b0790ab", "totalAmountOut": "21041810000000000000", "legs": [ { "adapter": "9inchV2", "amountIn": "2100000000", "amountOut": "21041810000000000000" } ] } ] } ] }, // Transaction object — only present when `sender` is provided "tx": { "to": "0x69033829f50244FD1be7BDC8e74aE0fF97E47126", "data": "0x...", // ABI-encoded goSwitch() calldata with feeOnOutput = false "value": "0" // "0" for ERC-20 input; amountIn for native PLS input }, // Same swap but with fee taken from the output token instead "txFeeOnOutput": { "to": "0x69033829f50244FD1be7BDC8e74aE0fF97E47126", "data": "0x...", // ABI-encoded goSwitch() calldata with feeOnOutput = true "value": "0" } }