@swapdk/wagmidk
v0.2.0-alpha.0
Published
Headless React hooks for cross-chain swaps in wagmi-native apps. Quote, sign, broadcast, track — against the SwapDK swap-engine HTTP API.
Maintainers
Readme
WagmiDK
WagmiDK (
@swapdk/wagmidk) is an unofficial library for using SwapDK from apps built with wagmi. Not affiliated with or endorsed by the wagmi project.
Headless React hooks for cross-chain swaps in wagmi-native apps — quote, sign, broadcast, track — without adopting the WDK wallet stack. Targets developers already building on wagmi + viem who want to add cross-chain swap functionality without changing their wallet integration strategy.
Status
v0.1.0-alpha.2 — first source release with mainnet validation. Public hook surface is feature-complete against the RFC-001 design and the ETH-Arbitrum → BTC path is mainnet-validated end-to-end. Expect breaking changes through 0.x as real-world usage shakes out additional edge cases. Pinned versions of peer dependencies, wider chain support, ERC-20 / TRC-20 path validation, and CHAINFLIP execution land iteratively.
Install
npm install @swapdk/wagmidk
# also required as peers (likely already in your wagmi app):
npm install wagmi viem @tanstack/react-query react @swapdk/swap-engine-clientCompatible with wagmi ≥ 2, viem ≥ 2, @tanstack/react-query ≥ 5, react ≥ 18, @swapdk/swap-engine-client ≥ 0.2.
Quickstart
Wrap your app in <SwapDKProvider> alongside the wagmi and react-query providers, then call useSwap with the source / destination endpoints. The convenience hook gives you a single phase field to branch on — quote loading, chain switching, signing, tracking — without orchestrating four primary hooks yourself.
import { WagmiProvider, createConfig, http } from 'wagmi'
import { arbitrum, mainnet } from 'wagmi/chains'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { SwapDKProvider, useSwap } from '@swapdk/wagmidk'
const wagmiConfig = createConfig({
chains: [mainnet, arbitrum],
transports: { [mainnet.id]: http(), [arbitrum.id]: http() },
})
const queryClient = new QueryClient()
function App() {
return (
<WagmiProvider config={wagmiConfig}>
<QueryClientProvider client={queryClient}>
<SwapDKProvider
baseURL="https://swap.example.com"
apiKey={process.env.NEXT_PUBLIC_SWAPDK_KEY!}
>
<BtcSwap />
</SwapDKProvider>
</QueryClientProvider>
</WagmiProvider>
)
}
function BtcSwap() {
const [btcAddress, setBtcAddress] = useState('')
const swap = useSwap({
from: { chainId: 42161, token: 'native', amount: 1_000_000_000_000_000n }, // 0.001 ETH on Arbitrum
to: { chain: 'bitcoin' },
recipient: btcAddress,
enabled: btcAddress.length > 20,
})
if (swap.error) return <ErrorBlock err={swap.error} />
if (swap.phase === 'quoting') return <Loading />
if (swap.phase === 'switch-chain') return <Button onClick={swap.switchChain}>Switch to Arbitrum</Button>
if (swap.phase === 'completed') return <Success legs={swap.legs!} />
if (swap.phase === 'pending'
|| swap.phase === 'executing') return <Tracker status={swap.status} legs={swap.legs} />
return (
<Button disabled={!swap.quote} onClick={swap.execute}>
Swap for {formatBtc(swap.quote?.to.estimatedAmount)} BTC
</Button>
)
}Need finer control (split quote and execute across components, custom react-query options, SSR prefetch)? Drop down to the primary hooks: useSwapQuote, useSwapExecute, useSwapStatus, useSourceChain. They each return idiomatic react-query shapes ({ data, isLoading, error, refetch } for queries, { mutate, isPending, error, data } for mutations). See docs/recipes/multi-step-ui.md for the breakdown.
Choosing useSwap vs primary hooks
| If your app looks like… | Use |
|---|---|
| One component shows price, switch, sign, status — drop-in swap UI | useSwap |
| Quote preview in one component, sign button in another | Primary hooks (see multi-step-ui recipe) |
| You need custom react-query options per stage (e.g. different gcTime for quote) | Primary hooks |
| You want server-side quote prefetching for SSR pages | Primary hooks (see ssr-prefetch recipe) |
| The phase state machine doesn't match your UI flow | Primary hooks |
useSwap is implemented as a thin wrapper over the primary hooks — switching between layers is reversible and tree-shaking lets you import just one or the other.
Recipes
Focused docs for common patterns under docs/recipes/:
| Recipe | When |
|---|---|
| erc20-source.md | ERC-20 token as source (USDC, USDT, …) |
| same-chain-evm.md | Swap within one EVM chain |
| multi-step-ui.md | Spread the swap across multiple components |
| error-handling.md | Render SwapDKError codes as user UI |
| ssr-prefetch.md | Server-side quote prefetch |
| troubleshooting.md | Common errors + how to fix them |
Public API
| Hook | Shape | Purpose |
|---|---|---|
| useSwapQuote(params) | query | Fetch a quote for a source → destination pair. Returns { quote, isLoading, isFetching, error, refetch, isStale }. |
| useSwapExecute() | mutation | Sign + broadcast via walletClient.sendTransaction. Returns { execute, isPending, error, data, reset }. Pre-flight checks throw typed SwapDKUserError codes (wallet_not_connected, wrong_chain, quote_expired). |
| useSwapStatus(params) | polled query | Track in-flight swap via /track. Returns { status, legs, outboundTxHash, isLoading, error }. Polling cadence: 5s → 10s → 20s → 40s → 80s doubling cap, stops on terminal status. |
| useSourceChain(quote) | helper | Surfaces the gap between connected chain and the chain quote.from.chainId requires. Returns { isCorrectChain, requiredChainId, switchChain }. |
| useSwap(params) | convenience | Synthesises the four primaries with a derived phase state machine. Best for "drop in, get a swap working" — drop down to primaries when you need finer control. |
| <SwapDKProvider> | provider | Configures swap-engine endpoint, API key, defaults. Must wrap consumer trees. |
Types worth knowing: SourceEndpoint (flat — always EVM source), DestinationEndpoint (discriminated union — evm / bitcoin / tron), SwapQuote, SwapExecution, SwapStatus, SwapPhase.
Error hierarchy: SwapDKError base; subclasses for User, Provider, Api, Network errors. WagmiDK-side errors (wallet-not-connected, etc.) surface as SwapDKUserError with a typed code literal — see WagmiDKErrorCode.
Where this fits in SwapDK
SwapDK ships across two distribution channels, both backed by the same swap-engine HTTP service aggregating THORChain, MAYAChain, and Chainflip routes. The channels diverge only at the React/wallet integration boundary.
- WDK channel — for apps built on Tether's WDK wallet kit. Ships as
@swapdk/wdk-protocol-bridge-swapdk-*packages on npm. - wagmi channel — this repo. For apps already on
wagmi+viem. Ships as@swapdk/wagmidk.
Both channels share @swapdk/swap-engine-client (HTTP client + zod-validated wire schemas + error hierarchy). The wagmi channel exists because adopting WDK is a non-starter for the typical wagmi-using React developer; WagmiDK reaches that audience using the wallet stack they already have. See docs/vision.md for the longer rationale.
Documentation
| Document | Purpose |
|---|---|
| docs/recipes/ | Cookbook — ERC-20, same-chain, multi-step UI, error handling, SSR, troubleshooting |
| CHANGELOG.md | Per-release notes with breaking changes called out |
| llms.txt + llms-full.txt | AI-agent-targeted documentation per the llmstxt.org convention. llms.txt is the navigable index; llms-full.txt is the concatenated corpus. Feed either to a coding assistant to brief it on WagmiDK. |
Internal design rationale (RFCs, ADRs, vision/positioning) is maintained separately by the SwapDK team and isn't part of this repository — those documents capture team-internal trade-offs and aren't useful to consumers.
Known limitations in v0.1.0-alpha
- Source chain coverage: 7 main EVM chains (Ethereum, Arbitrum, Base, BSC, Avalanche, Optimism, Polygon). Others throw a clear error at the asset-map boundary; add via
registerTokenfrom@swapdk/swap-engine-client. - ERC-20 / TRC-20 registry: only tokens already in
@swapdk/swap-engine-client'sKNOWN_TOKENSare supported. Common entries (USDC / USDT / WETH / WBTC / DAI / wrapped natives) ship preconfigured; custom tokens needregisterToken(...)at boot. - CHAINFLIP routes: filtered out at quote level in 0.1.x — swap-engine's
/swapendpoint doesn't execute Chainflip routes (deposit-channel flow needed; see ADR-012). End-to-end CHAINFLIP support lands in 0.2.x. Trade-off: occasional pricing/latency edge lost on the lifetime of 0.1.x. - ERC-20 approvals: not automatically handled in
useSwapExecute. Consumers approve via viem'suseWriteContractbefore callingswap.execute(). Seedocs/recipes/erc20-source.md. Automatic handling planned for 0.2.x. - EIP-7702 batched calls: not yet exposed in the surface. wagmi v2's
useSendCallsintegration is planned for a future minor. - SSR: works for quote-prefetching via TanStack Query's standard SSR patterns; execution requires browser context (wallet). See
docs/recipes/ssr-prefetch.md. - WalletClient typing: a single-step
as ConnectedWalletClientassertion inuseSwapExecuteis a known structural workaround pending an upstream wagmi-exported "connected wallet client" type. No runtime impact.
Contributing
WagmiDK is currently an SwapDK-team initiative. Bug reports, feature requests, and pull requests welcome at the public GitHub mirror: https://github.com/Swap-DK/wagmidk.
License
MIT — see LICENSE.
