WDK Swidge protocol module for EVM token swaps via the 0x Swap API v2.
This module implements the SwidgeProtocol interface from @tetherto/wdk-wallet, letting any WDK-based wallet perform same-chain EVM swaps through 0x's aggregated liquidity — with automatic ERC-20 approval handling and on-chain status tracking.
Note: Cross-chain bridging via 0x is not yet supported.
toChainmust equal the source chain.
npm install @0x/wdk-protocol-swidge-0x| Option | Type | Required | Description |
|---|---|---|---|
chainId |
number | string |
✅ | EVM chain ID of the bound wallet account |
apiKey |
string |
✅ | 0x API key — get one at dashboard.0x.org |
baseUrl |
string |
API base URL. Defaults to https://api.0x.org |
|
defaultSlippage |
number |
Default slippage as a decimal (e.g. 0.005 = 0.5%). Defaults to no slippage param sent |
|
skipApproval |
boolean |
Skip automatic ERC-20 approval before swapping | |
maxNetworkFeeBps |
number | bigint |
Maximum network fee in basis points of the input amount | |
maxProtocolFeeBps |
number | bigint |
Maximum protocol fee in basis points of the input amount |
Store your API key in an environment variable — never commit it to source control:
# .env
ZERO_EX_API_KEY=your_api_key_hereimport ZeroExProtocol from '@0x/wdk-protocol-swidge-0x'
const USDC = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'
const WETH = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2'
// ── 1. Get an indicative quote (no wallet needed) ──────────────────────────
const protocol = new ZeroExProtocol(undefined, {
chainId: 1, // Ethereum mainnet
apiKey: process.env.ZERO_EX_API_KEY,
defaultSlippage: 0.005 // 0.5%
})
const quote = await protocol.quoteSwidge({
fromToken: USDC,
toToken: WETH,
fromTokenAmount: 100_000_000n // 100 USDC (6 decimals)
})
console.log('Estimated WETH out:', quote.toTokenAmount)
console.log('Minimum WETH out: ', quote.toTokenAmountMin)
// ── 2. Execute (requires a full WDK EVM wallet account) ───────────────────
import WalletManagerEvm from '@tetherto/wdk-wallet-evm'
const manager = new WalletManagerEvm(process.env.MNEMONIC, {
provider: process.env.ETH_RPC,
chainId: 1
})
const account = await manager.getAccount(0)
const execProtocol = new ZeroExProtocol(account, {
chainId: 1,
apiKey: process.env.ZERO_EX_API_KEY,
defaultSlippage: 0.005
})
const result = await execProtocol.swidge({
fromToken: USDC,
toToken: WETH,
fromTokenAmount: 100_000_000n
})
console.log('Swap submitted:', result.hash)
// ── 3. Poll for status ────────────────────────────────────────────────────
let status
do {
await new Promise(r => setTimeout(r, 3000))
const s = await execProtocol.getSwidgeStatus(result.id)
status = s.status
console.log('Status:', status)
} while (status === 'pending')See examples/swap-usdc-to-weth.js for a runnable example.
| Chain | Chain ID |
|---|---|
| Abstract | 2741 |
| Arbitrum One | 42161 |
| Avalanche C-Chain | 43114 |
| Base | 8453 |
| Berachain | 80094 |
| BNB Smart Chain | 56 |
| Ethereum | 1 |
| HyperEVM | 999 |
| Ink | 57073 |
| Linea | 59144 |
| Mantle | 5000 |
| Monad | 143 |
| OP Mainnet | 10 |
| Plasma | 9745 |
| Polygon | 137 |
| Scroll | 534352 |
| Sonic | 146 |
| Tempo | 4217 |
| Unichain | 130 |
| World Chain | 480 |
Call getSupportedChains() to retrieve the full list at runtime.
The 0x Swap API accepts any liquid ERC-20 token by contract address. There is no supported token list — getSupportedTokens() throws NotImplementedError. Pass token addresses directly to quoteSwidge and swidge.
For native ETH (or any chain's native token), use 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE or one of the aliases 'native', 'eth', '', or the zero address. Aliases are matched case-insensitively and rewritten to the sentinel before the request is sent. Native tokens are supported as both the sell token and the buy token.
Note that 'eth' and '' mean the chain's native token on every supported chain — on Polygon, 'eth' resolves to POL. Any other value is forwarded to the 0x API unchanged; the module does not validate or checksum non-native token addresses.
| On-chain state | SwidgeStatus |
|---|---|
| Transaction unknown to the network | throws ZeroExUnknownTransactionError |
| Transaction known, no receipt yet | pending |
| Receipt with success status | completed |
| Receipt with reverted status | failed |
The module has no server-side status endpoint; status is resolved from on-chain state via the bound wallet account.
When the account exposes
getTransactionByHash, transaction existence is checked first so a well-formed id that the network has no record of throwsZeroExUnknownTransactionErrorinstead of reportingpendingforever. The receipt then resolvescompleted/failed, orpendingwhile in-flight.Fallback: if the account exposes only
getTransactionReceipt, an unknown transaction cannot be distinguished from an in-flight one, so status is derived from the receipt alone and reported aspendingwhen no receipt exists yet. If the account exposes neither method, status is alwayspending.
| 0x response field | SwidgeFeeType |
Notes | Legacy mapping |
|---|---|---|---|
totalNetworkFee |
network |
Denominated in the chain's native token (e.g. ETH) | fee |
fees.zeroExFee |
protocol |
0x protocol fee | bridgeFee |
fees.integratorFee |
affiliate |
Integrator / referral fee | not visible |
Fee caps (maxNetworkFeeBps, maxProtocolFeeBps) are expressed in basis points of the sell-token input amount and enforced fail-closed — a configured cap that cannot be evaluated rejects the swap with ZeroExFeeLimitExceededError:
maxNetworkFeeBps— the network fee is always denominated in the chain's native token. When the sell token is the native token the comparison is direct; otherwise the fee is converted into sell-token terms via one extra 0x/pricerequest (native → sell token). If no conversion route is available, the swap is rejected.maxProtocolFeeBps— compared directly when the protocol fee token matches the sell token (the common case). If the fee comes back denominated in another token it cannot be compared and the swap is rejected.
| Class | When thrown |
|---|---|
ZeroExApiError |
The 0x API returned a non-2xx response |
ZeroExInsufficientLiquidityError |
liquidityAvailable: false in the price response |
ZeroExFeeLimitExceededError |
A quoted fee exceeds a configured maxNetworkFeeBps / maxProtocolFeeBps cap, or a configured cap cannot be evaluated (fail-closed) |
ZeroExReadOnlyError |
swidge is called without a full signing account |
ZeroExValidationError |
Invalid or missing input parameters |
ZeroExUnsupportedOperationError |
An unsupported operation is requested (e.g. cross-chain toChain) |
ZeroExUnknownTransactionError |
getSwidgeStatus is called for a transaction the network has no record of |
ZeroExTransactionRevertedError |
The swap transaction reverted on-chain |
ZeroExTimeoutError |
Timed out waiting for transaction confirmation |
NotImplementedError |
getSupportedTokens() is called |
This module implements SwidgeProtocol from @tetherto/wdk-wallet ^1.0.0-beta.11.
The swap, quoteSwap, bridge, and quoteBridge methods are inherited from the base class and delegate to swidge / quoteSwidge respectively.
npm install # install dependencies
npm test # run unit tests
npm run lint # check code style (JavaScript Standard Style)
npm run build:types # generate TypeScript declarations in types/End-to-end test against the live 0x API (read-only, no wallet needed):
ZERO_EX_API_KEY=... npm run e2eTo test real swap execution, add a BIP-39 mnemonic:
ZERO_EX_API_KEY=... MNEMONIC="word word ..." npm run e2eEnd-to-end tested against the live 0x API on Base mainnet (chain 8453):
| Swap | Chain | Tx |
|---|---|---|
| 1 USDC → WETH | Base | 0x87afe3…704ec |
The following fields from SwidgeCommonOptions are accepted by the interface but not supported by this module:
| Option | Reason |
|---|---|
toChain |
Cross-chain bridging is not implemented. Passing a toChain that differs from the configured chainId throws an error. |
refundAddress |
The 0x AllowanceHolder flow has no refund path. This field is silently ignored. |
The 0x Swap API enforces rate limits based on your plan tier:
| Plan | Rate limit |
|---|---|
| Standard (free) | 5 requests per second |
| Custom | Higher limits available on request |
Limits are enforced on fixed 1-second windows across all endpoints combined. The API returns HTTP 429 Too Many Requests when the limit is exceeded.
See the 0x rate limits documentation for details and to discuss higher limits.
Open an issue at github.com/0xProject/wdk-protocol-swidge-0x/issues.
See SECURITY.md for the vulnerability disclosure process.
Apache 2.0 — see LICENSE.