@shapeshiftoss/swap-widget 0.3.0 → 0.5.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 (2) hide show
  1. package/README.md +366 -366
  2. package/package.json +4 -5
package/README.md CHANGED
@@ -1,278 +1,300 @@
1
1
  # @shapeshiftoss/swap-widget
2
2
 
3
- An embeddable React widget that enables multi-chain token swaps using ShapeShift's aggregation API. Integrate swaps into your application with minimal configuration.
3
+ An embeddable React widget that enables multi-chain token swaps using ShapeShift's aggregation API. Drop it into your app to offer EVM, UTXO, and Solana swaps with minimal configuration.
4
+
5
+ > **This README is the canonical reference for the swap widget.** Other docs (including the ShapeShift Public API docs) link here.
4
6
 
5
7
  ## Table of Contents
6
8
 
7
9
  - [Installation](#installation)
8
10
  - [Quick Start](#quick-start)
11
+ - [Wallet Connection](#wallet-connection)
9
12
  - [Props Reference](#props-reference)
13
+ - [Filtering Chains and Assets](#filtering-chains-and-assets)
10
14
  - [Theming](#theming)
11
15
  - [Examples](#examples)
12
16
  - [Exported Types](#exported-types)
13
17
  - [Exported Utilities](#exported-utilities)
14
18
  - [Exported Hooks](#exported-hooks)
15
19
  - [Supported Chains](#supported-chains)
20
+ - [Supported Swappers](#supported-swappers)
21
+ - [Partner Codes & Affiliate Revenue](#partner-codes--affiliate-revenue)
16
22
  - [Notes and Limitations](#notes-and-limitations)
17
23
 
18
24
  ## Installation
19
25
 
20
26
  ```bash
21
- pnpm add @shapeshiftoss/swap-widget
22
- # or
23
27
  npm install @shapeshiftoss/swap-widget
24
28
  ```
25
29
 
26
30
  ### Peer Dependencies
27
31
 
28
- This package requires React 18 or later:
32
+ The widget relies on React, wagmi/viem, React Query, and Reown AppKit (used internally for wallet
33
+ connection). The widget initializes AppKit with the EVM, Bitcoin, and Solana adapters at load, so all
34
+ of these peers are required — install them alongside the package:
29
35
 
30
- ```json
31
- {
32
- "peerDependencies": {
33
- "react": ">=18.0.0",
34
- "react-dom": ">=18.0.0"
35
- }
36
- }
36
+ ```bash
37
+ npm install react react-dom \
38
+ wagmi @wagmi/core viem \
39
+ @tanstack/react-query \
40
+ @reown/appkit @reown/appkit-adapter-wagmi \
41
+ @reown/appkit-adapter-bitcoin @reown/appkit-adapter-solana \
42
+ @solana/wallet-adapter-wallets @solana/web3.js
43
+ ```
44
+
45
+ - **React 18 or 19** is supported (`^18.0.0 || ^19.0.0`).
46
+
47
+ ### Import the stylesheet
48
+
49
+ The widget ships a stylesheet that **must** be imported once for it to render correctly:
50
+
51
+ ```tsx
52
+ import '@shapeshiftoss/swap-widget/style.css'
37
53
  ```
38
54
 
39
55
  ## Quick Start
40
56
 
41
57
  ```tsx
42
- import { SwapWidget } from "@shapeshiftoss/swap-widget";
58
+ import '@shapeshiftoss/swap-widget/style.css'
59
+
60
+ import { SwapWidget } from '@shapeshiftoss/swap-widget'
43
61
 
44
62
  function App() {
45
63
  return (
46
64
  <SwapWidget
65
+ // Initializes the built-in wallet connection. Optional if your app already
66
+ // initializes Reown AppKit — see "Wallet Connection" below.
67
+ walletConnectProjectId="your-walletconnect-project-id"
68
+ // Optional: attribute swaps to your affiliate account.
47
69
  partnerCode="your-partner-code"
48
70
  theme="dark"
49
- onSwapSuccess={(txHash) => console.log("Success:", txHash)}
50
- onSwapError={(error) => console.error("Error:", error)}
71
+ onSwapSuccess={txHash => console.log('Success:', txHash)}
72
+ onSwapError={error => console.error('Error:', error)}
51
73
  />
52
- );
74
+ )
53
75
  }
54
76
  ```
55
77
 
56
- ## Props Reference
78
+ ## Wallet Connection
57
79
 
58
- ### SwapWidgetProps
59
-
60
- | Prop | Type | Default | Description |
61
- | ------------------------ | ----------------------------------------------- | ---------------- | ---------------------------------------------------------------------------------------------------------- |
62
- | `partnerCode` | `string` | - | Your registered partner code for affiliate fee attribution. Register at the affiliate dashboard. |
63
- | `apiBaseUrl` | `string` | - | Custom API base URL. Useful for testing or custom deployments. |
64
- | `defaultSellAsset` | `Asset` | ETH on Ethereum | Initial asset to sell. |
65
- | `defaultBuyAsset` | `Asset` | USDC on Ethereum | Initial asset to buy. |
66
- | `disabledChainIds` | `ChainId[]` | `[]` | Chain IDs to hide from the asset selector. |
67
- | `disabledAssetIds` | `AssetId[]` | `[]` | Asset IDs to hide from the asset selector. |
68
- | `allowedChainIds` | `ChainId[]` | - | If provided, only show assets from these chains. Use this to restrict the widget to specific chains. |
69
- | `allowedAssetIds` | `AssetId[]` | - | If provided, only show these specific assets. |
70
- | `walletClient` | `WalletClient` | - | Viem wallet client for executing EVM transactions. |
71
- | `onConnectWallet` | `() => void` | - | Callback when user clicks "Connect Wallet" button. |
72
- | `onSwapSuccess` | `(txHash: string) => void` | - | Callback when a swap transaction succeeds. |
73
- | `onSwapError` | `(error: Error) => void` | - | Callback when a swap transaction fails. |
74
- | `onAssetSelect` | `(type: "sell" \| "buy", asset: Asset) => void` | - | Callback when user selects an asset. |
75
- | `theme` | `ThemeMode \| ThemeConfig` | `"dark"` | Theme mode (`"light"` or `"dark"`) or full theme configuration. |
76
- | `defaultSlippage` | `string` | `"0.5"` | Default slippage tolerance percentage. |
77
- | `showPoweredBy` | `boolean` | `true` | Show "Powered by ShapeShift" branding. |
78
- | `enableWalletConnection` | `boolean` | `false` | Enable built-in wallet connection UI using Reown AppKit. Supports EVM, Bitcoin, and Solana wallets. Requires `walletConnectProjectId`. |
79
- | `walletConnectProjectId` | `string` | - | WalletConnect project ID for the built-in wallet connection. Get one at <https://cloud.walletconnect.com>. |
80
- | `defaultReceiveAddress` | `string` | - | Fixed receive address for swaps. When set, users cannot change the receive address. |
80
+ The widget connects wallets through [Reown AppKit](https://reown.com/appkit)
81
+ and provides the `WagmiProvider` / `QueryClient` it needs — you don't wrap it in your own.
81
82
 
82
- ## Theming
83
+ ### Initializing AppKit
83
84
 
84
- The widget supports both simple theme modes and full customization.
85
+ **The widget renders nothing until AppKit is initialized.** There are two ways to satisfy this:
85
86
 
86
- ### Simple Theme Mode
87
+ - **Let the widget initialize AppKit (default).** Pass `walletConnectProjectId` and the widget creates
88
+ and owns its own AppKit instance. Get a free project ID at <https://dashboard.reown.com>.
87
89
 
88
- ```tsx
89
- <SwapWidget theme="dark" />
90
- // or
91
- <SwapWidget theme="light" />
92
- ```
90
+ ```tsx
91
+ <SwapWidget walletConnectProjectId="your-walletconnect-project-id" />
92
+ ```
93
93
 
94
- ### Custom Theme Configuration
94
+ - **Reuse your app's existing AppKit.** If your host app already calls `createAppKit()` (with a wagmi
95
+ EVM adapter), omit `walletConnectProjectId`. The widget detects the shared AppKit singleton, reads the
96
+ wagmi config off it, and provides its own `WagmiProvider` / `QueryClient` from that config — **you wrap
97
+ the widget in no providers of your own.** Pair this with `showConnectButton={false}` to drive
98
+ connection entirely from your own UI. See `src/demo/ExternalWalletApp.tsx` for a full host example.
95
99
 
96
- ```tsx
97
- import { SwapWidget } from "@shapeshiftoss/swap-widget";
98
- import type { ThemeConfig } from "@shapeshiftoss/swap-widget";
100
+ Two requirements for this mode:
99
101
 
100
- const customTheme: ThemeConfig = {
101
- mode: "dark",
102
- accentColor: "#3861fb", // Primary accent color (buttons, focus states)
103
- backgroundColor: "#0a0a14", // Widget background
104
- cardColor: "#12121c", // Card/panel background
105
- textColor: "#ffffff", // Primary text color
106
- borderRadius: "12px", // Border radius for elements
107
- fontFamily: "Inter, sans-serif",
108
- };
102
+ - **Initialize AppKit _before_ the widget mounts.** The widget reads the AppKit singleton when it
103
+ mounts and does not poll for late initialization — if AppKit isn't up yet, the widget renders nothing.
104
+ - **Dedupe the AppKit/wagmi packages.** `@reown/appkit*`, `wagmi`, and `viem` must resolve to a single
105
+ shared copy in your app, so the widget and your app share one AppKit instance and one wagmi state. A
106
+ duplicated copy means the widget reads an empty store and shows no connection.
109
107
 
110
- function App() {
111
- return <SwapWidget theme={customTheme} />;
112
- }
113
- ```
108
+ The header shows a built-in **Connect** button by default; set `showConnectButton={false}` to hide it
109
+ and drive connection from your own UI.
114
110
 
115
- ### ThemeConfig Properties
111
+ ### Supported wallet namespaces
116
112
 
117
- | Property | Type | Description |
118
- | ----------------- | ------------------- | -------------------------------------------------- |
119
- | `mode` | `"light" \| "dark"` | Base theme mode. Required. |
120
- | `accentColor` | `string` | Primary accent color for buttons and focus states. |
121
- | `backgroundColor` | `string` | Widget background color. |
122
- | `cardColor` | `string` | Card and panel background color. |
123
- | `textColor` | `string` | Primary text color. |
124
- | `borderRadius` | `string` | Border radius for UI elements. |
125
- | `fontFamily` | `string` | Font family for the widget. |
113
+ Once connected, the widget can sign and broadcast transactions for three wallet namespaces:
126
114
 
127
- ## Examples
115
+ | Namespace | Chains | Example wallets |
116
+ | --------- | ------------------------------- | -------------------------------- |
117
+ | `eip155` | All supported EVM chains | MetaMask, WalletConnect, Rabby |
118
+ | `bip122` | Bitcoin and other UTXO chains | WalletConnect-compatible wallets |
119
+ | `solana` | Solana | Phantom, Solflare |
128
120
 
129
- ### Basic Usage
121
+ The header shows a **Connect** button by default (toggle with `showConnectButton`) that opens the
122
+ AppKit modal. Swaps whose sell asset is not in an executable namespace (see
123
+ [Supported Chains](#supported-chains)) redirect to [app.shapeshift.com](https://app.shapeshift.com)
124
+ when `allowShapeshiftRedirect` is enabled.
130
125
 
131
- ```tsx
132
- import { SwapWidget } from "@shapeshiftoss/swap-widget";
126
+ ## Props Reference
133
127
 
134
- function App() {
135
- return <SwapWidget partnerCode="your-partner-code" theme="dark" />;
128
+ ### `SwapWidgetProps`
129
+
130
+ | Prop | Type | Default | Description |
131
+ | ------------------------ | ----------------------------------------------- | ------------------ | -------------------------------------------------------------------------------------------------------- |
132
+ | `walletConnectProjectId` | `string` | – | Reown AppKit / WalletConnect project ID. The widget uses it to initialize AppKit. Required unless your host app already initializes AppKit (see [Wallet Connection](#wallet-connection)). |
133
+ | `partnerCode` | `string` | – | Your registered partner code for affiliate fee attribution. See [Partner Codes](#partner-codes--affiliate-revenue). |
134
+ | `apiBaseUrl` | `string` | `https://api.shapeshift.com` | Override the API base URL. Useful for testing or custom deployments. |
135
+ | `defaultSellAsset` | `Asset` | ETH on Ethereum | Initial asset to sell. |
136
+ | `defaultBuyAsset` | `Asset` | USDC on Ethereum | Initial asset to buy. |
137
+ | `sellFilters` | `SwapWidgetFilters` | `{}` | Restrict which chains/assets are selectable for the **sell** side. See [Filtering](#filtering-chains-and-assets). |
138
+ | `buyFilters` | `SwapWidgetFilters` | `{}` | Restrict which chains/assets are selectable for the **buy** side. |
139
+ | `allowedSwapperNames` | `SwapperName[]` | all enabled | Limit quotes to specific swappers. See [Supported Swappers](#supported-swappers). |
140
+ | `allowShapeshiftRedirect`| `boolean` | `true` | When a swap isn't executable in-widget, redirect to app.shapeshift.com instead of hiding it. |
141
+ | `isBuyAssetLocked` | `boolean` | `false` | Prevent the user from changing the buy asset. |
142
+ | `theme` | `ThemeMode \| ThemeConfig` | `"dark"` | Theme mode (`"light"` or `"dark"`) or a full theme configuration object. See [Theming](#theming). |
143
+ | `defaultSlippage` | `string` | `"0.5"` | Default slippage tolerance, as a percentage string. |
144
+ | `showPoweredBy` | `boolean` | `true` | Show the "Powered by ShapeShift" footer. |
145
+ | `showConnectButton` | `boolean` | `true` | Show the built-in Connect button in the widget header. |
146
+ | `ratesRefetchInterval` | `number` | `15000` | How often (ms) to refetch swap rates. |
147
+ | `onSwapSuccess` | `(txHash: string) => void` | – | Called when a swap transaction succeeds. |
148
+ | `onSwapError` | `(error: Error) => void` | – | Called when a swap transaction fails. |
149
+
150
+ ## Filtering Chains and Assets
151
+
152
+ Restrict the sell and/or buy asset selectors independently via the `sellFilters` and `buyFilters`
153
+ props. Both accept the same shape:
154
+
155
+ ```typescript
156
+ type SwapWidgetFilters = {
157
+ allowedChainIds?: ChainId[] // If set, only these chains are selectable
158
+ disabledChainIds?: ChainId[] // Hide these chains
159
+ allowedAssetIds?: AssetId[] // If set, only these assets are selectable
160
+ disabledAssetIds?: AssetId[] // Hide these assets
136
161
  }
137
162
  ```
138
163
 
139
- ### With External Wallet Connection (wagmi/viem)
140
-
141
- If you already have wagmi set up in your application, you can pass the wallet client directly:
142
-
143
164
  ```tsx
144
- import { SwapWidget } from "@shapeshiftoss/swap-widget";
145
- import { useWalletClient } from "wagmi";
165
+ import { EVM_CHAIN_IDS, SwapWidget } from '@shapeshiftoss/swap-widget'
146
166
 
147
167
  function App() {
148
- const { data: walletClient } = useWalletClient();
149
-
150
168
  return (
151
169
  <SwapWidget
170
+ walletConnectProjectId="your-walletconnect-project-id"
152
171
  partnerCode="your-partner-code"
153
- walletClient={walletClient}
154
- onConnectWallet={() => {
155
- // Your custom wallet connection logic
156
- }}
157
- onSwapSuccess={(txHash) => {
158
- console.log("Swap successful:", txHash);
159
- }}
160
- onSwapError={(error) => {
161
- console.error("Swap failed:", error);
172
+ // Only allow selling ETH-chain, Polygon, and Arbitrum assets
173
+ sellFilters={{
174
+ allowedChainIds: [EVM_CHAIN_IDS.ethereum, EVM_CHAIN_IDS.polygon, EVM_CHAIN_IDS.arbitrum],
162
175
  }}
163
- theme={{
164
- mode: "dark",
165
- accentColor: "#3861fb",
166
- backgroundColor: "#0a0a14",
167
- cardColor: "#12121c",
176
+ // Hide a specific buy token
177
+ buyFilters={{
178
+ disabledAssetIds: ['eip155:1/erc20:0x...'],
168
179
  }}
180
+ theme="dark"
169
181
  />
170
- );
182
+ )
171
183
  }
172
184
  ```
173
185
 
174
- ### With Custom Default Assets
186
+ ## Theming
187
+
188
+ The widget supports a simple light/dark mode or a full theme configuration object.
189
+
190
+ ### Simple theme mode
175
191
 
176
192
  ```tsx
177
- import { SwapWidget } from "@shapeshiftoss/swap-widget";
178
- import type { Asset } from "@shapeshiftoss/swap-widget";
193
+ <SwapWidget walletConnectProjectId="..." theme="dark" />
194
+ // or
195
+ <SwapWidget walletConnectProjectId="..." theme="light" />
196
+ ```
179
197
 
180
- const defaultSellAsset: Asset = {
181
- assetId: "eip155:137/slip44:966",
182
- chainId: "eip155:137",
183
- symbol: "MATIC",
184
- name: "Polygon",
185
- precision: 18,
186
- icon: "https://example.com/matic.png",
187
- };
198
+ ### Custom theme configuration
188
199
 
189
- const defaultBuyAsset: Asset = {
190
- assetId: "eip155:137/erc20:0x2791bca1f2de4661ed88a30c99a7a9449aa84174",
191
- chainId: "eip155:137",
192
- symbol: "USDC",
193
- name: "USD Coin",
194
- precision: 6,
195
- icon: "https://example.com/usdc.png",
196
- };
200
+ ```tsx
201
+ import { SwapWidget } from '@shapeshiftoss/swap-widget'
202
+ import type { ThemeConfig } from '@shapeshiftoss/swap-widget'
203
+
204
+ const customTheme: ThemeConfig = {
205
+ mode: 'dark', // required
206
+ accentColor: '#3861fb',
207
+ backgroundColor: '#0a0a14',
208
+ cardColor: '#12121c',
209
+ textColor: '#ffffff',
210
+ borderRadius: '12px',
211
+ fontFamily: 'Inter, sans-serif',
212
+ buttonVariant: 'filled',
213
+ }
197
214
 
198
215
  function App() {
199
- return (
200
- <SwapWidget
201
- partnerCode="your-partner-code"
202
- defaultSellAsset={defaultSellAsset}
203
- defaultBuyAsset={defaultBuyAsset}
204
- theme="dark"
205
- />
206
- );
216
+ return <SwapWidget walletConnectProjectId="..." theme={customTheme} />
207
217
  }
208
218
  ```
209
219
 
210
- ### Restricting Available Chains and Assets
220
+ ### `ThemeConfig` properties
221
+
222
+ | Property | Type | Description |
223
+ | -------------------- | ------------------------ | ---------------------------------------------------- |
224
+ | `mode` | `"light" \| "dark"` | Base theme mode. **Required.** |
225
+ | `accentColor` | `string` | Primary accent color (buttons, focus states). |
226
+ | `backgroundColor` | `string` | Widget background color. |
227
+ | `cardColor` | `string` | Card / panel background color. |
228
+ | `textColor` | `string` | Primary text color. |
229
+ | `secondaryTextColor` | `string` | Secondary text color. |
230
+ | `mutedTextColor` | `string` | Muted/tertiary text color. |
231
+ | `inputColor` | `string` | Input field background color. |
232
+ | `hoverColor` | `string` | Hover background color. |
233
+ | `borderColor` | `string` | Border color. |
234
+ | `borderRadius` | `string` | Base border radius for UI elements (e.g. `"12px"`). |
235
+ | `fontFamily` | `string` | Font family for the widget. |
236
+ | `buttonVariant` | `"filled" \| "outline"` | Primary button style. |
211
237
 
212
- Use `allowedChainIds` to restrict the widget to only show specific chains. This is useful when you want to limit swaps to certain networks.
238
+ ## Examples
239
+
240
+ ### Basic usage
213
241
 
214
242
  ```tsx
215
- import { SwapWidget, EVM_CHAIN_IDS } from "@shapeshiftoss/swap-widget";
243
+ import { SwapWidget } from '@shapeshiftoss/swap-widget'
216
244
 
217
245
  function App() {
218
- return (
219
- <SwapWidget
220
- partnerCode="your-partner-code"
221
- allowedChainIds={[
222
- EVM_CHAIN_IDS.ethereum,
223
- EVM_CHAIN_IDS.polygon,
224
- EVM_CHAIN_IDS.arbitrum,
225
- ]}
226
- disabledAssetIds={[
227
- "eip155:1/erc20:0x...", // Hide specific tokens
228
- ]}
229
- theme="dark"
230
- />
231
- );
246
+ return <SwapWidget walletConnectProjectId="..." partnerCode="your-partner-code" theme="dark" />
232
247
  }
233
248
  ```
234
249
 
235
- ### With Built-in Wallet Connection (Multi-Chain)
236
-
237
- The widget can manage wallet connections internally using Reown AppKit, which supports EVM chains, Bitcoin, and Solana. This is useful when you don't have an existing wallet connection setup.
250
+ ### Custom default assets
238
251
 
239
252
  ```tsx
240
- import { SwapWidget } from "@shapeshiftoss/swap-widget";
253
+ import { SwapWidget } from '@shapeshiftoss/swap-widget'
254
+ import type { Asset } from '@shapeshiftoss/swap-widget'
255
+
256
+ const defaultSellAsset: Asset = {
257
+ assetId: 'eip155:137/slip44:966',
258
+ chainId: 'eip155:137',
259
+ symbol: 'POL',
260
+ name: 'Polygon',
261
+ precision: 18,
262
+ }
263
+
264
+ const defaultBuyAsset: Asset = {
265
+ assetId: 'eip155:137/erc20:0x2791bca1f2de4661ed88a30c99a7a9449aa84174',
266
+ chainId: 'eip155:137',
267
+ symbol: 'USDC',
268
+ name: 'USD Coin',
269
+ precision: 6,
270
+ }
241
271
 
242
272
  function App() {
243
273
  return (
244
274
  <SwapWidget
275
+ walletConnectProjectId="..."
245
276
  partnerCode="your-partner-code"
246
- enableWalletConnection={true}
247
- walletConnectProjectId="your-walletconnect-project-id"
277
+ defaultSellAsset={defaultSellAsset}
278
+ defaultBuyAsset={defaultBuyAsset}
248
279
  theme="dark"
249
280
  />
250
- );
281
+ )
251
282
  }
252
283
  ```
253
284
 
254
- When `enableWalletConnection` is true, the widget will:
255
- - Show a "Connect" button that opens the AppKit modal
256
- - Support connecting EVM wallets (MetaMask, WalletConnect, etc.)
257
- - Support connecting Bitcoin wallets via WalletConnect
258
- - Support connecting Solana wallets (Phantom, Solflare, etc.)
285
+ ### Locking the buy asset
259
286
 
260
- ### With Fixed Receive Address
261
-
262
- Use `defaultReceiveAddress` to lock the receive address. When set, users cannot change the destination address. This is useful for integrations where you want all swaps to go to a specific address.
287
+ Use `isBuyAssetLocked` so users can only change the sell side — useful when you want all swaps to
288
+ end in a specific token.
263
289
 
264
290
  ```tsx
265
- import { SwapWidget } from "@shapeshiftoss/swap-widget";
266
-
267
- function App() {
268
- return (
269
- <SwapWidget
270
- partnerCode="your-partner-code"
271
- defaultReceiveAddress="0x1234567890abcdef1234567890abcdef12345678"
272
- theme="dark"
273
- />
274
- );
275
- }
291
+ <SwapWidget
292
+ walletConnectProjectId="..."
293
+ partnerCode="your-partner-code"
294
+ defaultBuyAsset={defaultBuyAsset}
295
+ isBuyAssetLocked
296
+ theme="dark"
297
+ />
276
298
  ```
277
299
 
278
300
  ## Exported Types
@@ -281,246 +303,224 @@ function App() {
281
303
  import type {
282
304
  Asset,
283
305
  AssetId,
284
- ChainId,
285
306
  Chain,
286
- TradeRate,
287
- TradeQuote,
288
- SwapperName,
307
+ ChainId,
308
+ SwapWidgetFilters,
289
309
  SwapWidgetProps,
290
- ThemeMode,
291
310
  ThemeConfig,
292
- } from "@shapeshiftoss/swap-widget";
311
+ ThemeMode,
312
+ TradeQuote,
313
+ TradeRate,
314
+ } from '@shapeshiftoss/swap-widget'
293
315
  ```
294
316
 
295
- ### Asset
296
-
297
- ```typescript
298
- type Asset = {
299
- assetId: AssetId; // CAIP-19 format: "eip155:1/slip44:60"
300
- chainId: ChainId; // CAIP-2 format: "eip155:1"
301
- symbol: string; // e.g., "ETH"
302
- name: string; // e.g., "Ethereum"
303
- precision: number; // e.g., 18
304
- icon?: string; // URL to asset icon
305
- color?: string; // Brand color
306
- networkName?: string; // Display name for the network
307
- networkIcon?: string; // URL to network icon
308
- explorer?: string; // Block explorer URL
309
- explorerTxLink?: string; // Transaction explorer link template
310
- explorerAddressLink?: string; // Address explorer link template
311
- relatedAssetKey?: AssetId | null; // Related asset for bridged tokens
312
- };
313
- ```
317
+ `SwapperName` is exported as a runtime value (an `enum`) — import it from the value position, not as
318
+ a type.
314
319
 
315
- ### SwapperName
320
+ ### `Asset`
316
321
 
317
322
  ```typescript
318
- type SwapperName =
319
- | "THORChain"
320
- | "MAYAChain"
321
- | "CoW Swap"
322
- | "0x"
323
- | "Portals"
324
- | "Chainflip"
325
- | "Relay"
326
- | "Bebop"
327
- | "Jupiter"
328
- | "1inch"
329
- | "ButterSwap"
330
- | "ArbitrumBridge";
331
- ```
332
-
333
- ### TradeRate
334
-
335
- ```typescript
336
- type TradeRate = {
337
- swapperName: SwapperName;
338
- rate: string;
339
- buyAmountCryptoBaseUnit: string;
340
- sellAmountCryptoBaseUnit: string;
341
- steps: number;
342
- estimatedExecutionTimeMs?: number;
343
- affiliateBps: string;
344
- networkFeeCryptoBaseUnit?: string;
345
- error?: {
346
- code: string;
347
- message: string;
348
- };
349
- id?: string;
350
- };
323
+ type Asset = {
324
+ assetId: AssetId // CAIP-19, e.g. "eip155:1/slip44:60"
325
+ chainId: ChainId // CAIP-2, e.g. "eip155:1"
326
+ symbol: string // e.g. "ETH"
327
+ name: string // e.g. "Ethereum"
328
+ precision: number // e.g. 18
329
+ icon?: string
330
+ color?: string
331
+ networkName?: string
332
+ networkIcon?: string
333
+ explorer?: string
334
+ explorerTxLink?: string
335
+ explorerAddressLink?: string
336
+ relatedAssetKey?: AssetId | null
337
+ }
351
338
  ```
352
339
 
353
340
  ## Exported Utilities
354
341
 
355
342
  ```typescript
356
343
  import {
357
- isEvmChainId,
358
- getEvmChainIdNumber,
359
- getChainType,
360
- formatAmount,
361
- parseAmount,
362
- truncateAddress,
363
- EVM_CHAIN_IDS,
364
- UTXO_CHAIN_IDS,
365
344
  COSMOS_CHAIN_IDS,
345
+ EVM_CHAIN_IDS,
366
346
  OTHER_CHAIN_IDS,
367
- CHAIN_METADATA,
368
- getChainMeta,
369
- getChainName,
370
- getChainIcon,
347
+ REDIRECT_ONLY_CHAIN_IDS,
348
+ SwapperName,
349
+ UTXO_CHAIN_IDS,
350
+ formatAmount,
351
+ getBaseAsset,
371
352
  getChainColor,
372
- } from "@shapeshiftoss/swap-widget";
353
+ getChainIcon,
354
+ getChainName,
355
+ getChainType,
356
+ getEvmNetworkId,
357
+ getExplorerTxLink,
358
+ isEvmChainId,
359
+ isWidgetExecutableChainId,
360
+ isWidgetSupportedChainId,
361
+ parseAmount,
362
+ truncateAddress,
363
+ } from '@shapeshiftoss/swap-widget'
373
364
  ```
374
365
 
375
- ### Chain Type Utilities
376
-
377
- | Function | Signature | Description |
378
- | --------------------- | ------------------------------------------------------------------------- | ---------------------------------------------------- |
379
- | `isEvmChainId` | `(chainId: string) => boolean` | Check if a chain ID is an EVM chain. |
380
- | `getEvmChainIdNumber` | `(chainId: string) => number` | Extract the numeric chain ID from a CAIP-2 chain ID. |
381
- | `getChainType` | `(chainId: string) => "evm" \| "utxo" \| "cosmos" \| "solana" \| "other"` | Get the chain type from a chain ID. |
366
+ ### Chain helpers
382
367
 
383
- ### Amount Formatting
368
+ | Function | Signature | Description |
369
+ | --------------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------ |
370
+ | `isEvmChainId` | `(chainId: string) => boolean` | Whether a chain ID is an EVM chain. |
371
+ | `getEvmNetworkId` | `(chainId: string) => number` | Extract the numeric network ID from a CAIP-2 EVM chain ID. |
372
+ | `getChainType` | `(chainId: string) => "evm" \| "utxo" \| "cosmos" \| "solana" \| "other"` | Classify a chain by namespace. |
373
+ | `isWidgetSupportedChainId` | `(chainId: string) => boolean` | Whether the widget lists assets on this chain. |
374
+ | `isWidgetExecutableChainId` | `(chainId: string) => boolean` | Whether the widget can sign/execute swaps on this chain in-app. |
375
+ | `getChainName` | `(chainId: ChainId) => string` | Display name for a chain. |
376
+ | `getChainIcon` | `(chainId: ChainId) => string \| undefined` | Icon URL for a chain. |
377
+ | `getChainColor` | `(chainId: ChainId) => string` | Brand color for a chain. |
378
+ | `getBaseAsset` | `(chainId: ChainId) => Asset \| undefined` | Native asset for a chain. |
379
+ | `getExplorerTxLink` | `(chainId: ChainId) => string \| undefined` | Block-explorer transaction link template. |
384
380
 
385
- | Function | Signature | Description |
386
- | ----------------- | -------------------------------------------------------------------- | -------------------------------------------------------- |
387
- | `formatAmount` | `(amount: string, decimals: number, maxDecimals?: number) => string` | Format a base unit amount for display. |
388
- | `parseAmount` | `(amount: string, decimals: number) => string` | Parse a human-readable amount to base units. |
389
- | `truncateAddress` | `(address: string, chars?: number) => string` | Truncate an address for display (e.g., `0x1234...5678`). |
381
+ ### Amount and address formatting
390
382
 
391
- ### Chain Metadata
383
+ | Function | Signature | Description |
384
+ | ----------------- | -------------------------------------------------------------------- | ---------------------------------------------------- |
385
+ | `formatAmount` | `(amount: string, decimals: number, maxDecimals?: number) => string` | Format a base-unit amount for display. |
386
+ | `parseAmount` | `(amount: string, decimals: number) => string` | Parse a human-readable amount into base units. |
387
+ | `truncateAddress` | `(address: string, chars?: number) => string` | Truncate an address (e.g. `0x1234...5678`). |
392
388
 
393
- | Function | Signature | Description |
394
- | --------------- | ---------------------------------------------- | --------------------------------- |
395
- | `getChainMeta` | `(chainId: ChainId) => ChainMeta \| undefined` | Get full metadata for a chain. |
396
- | `getChainName` | `(chainId: ChainId) => string` | Get the display name for a chain. |
397
- | `getChainIcon` | `(chainId: ChainId) => string \| undefined` | Get the icon URL for a chain. |
398
- | `getChainColor` | `(chainId: ChainId) => string` | Get the brand color for a chain. |
399
-
400
- ### Chain ID Constants
389
+ ### Chain ID constants
401
390
 
402
391
  ```typescript
403
392
  const EVM_CHAIN_IDS = {
404
- ethereum: "eip155:1",
405
- arbitrum: "eip155:42161",
406
- optimism: "eip155:10",
407
- polygon: "eip155:137",
408
- base: "eip155:8453",
409
- avalanche: "eip155:43114",
410
- bsc: "eip155:56",
411
- gnosis: "eip155:100",
412
- };
393
+ ethereum: 'eip155:1',
394
+ arbitrum: 'eip155:42161',
395
+ optimism: 'eip155:10',
396
+ polygon: 'eip155:137',
397
+ base: 'eip155:8453',
398
+ avalanche: 'eip155:43114',
399
+ bsc: 'eip155:56',
400
+ gnosis: 'eip155:100',
401
+ monad: 'eip155:143',
402
+ megaEth: 'eip155:4326',
403
+ hyperEvm: 'eip155:999',
404
+ plasma: 'eip155:9745',
405
+ katana: 'eip155:747474',
406
+ }
413
407
 
414
408
  const UTXO_CHAIN_IDS = {
415
- bitcoin: "bip122:000000000019d6689c085ae165831e93",
416
- bitcoinCash: "bip122:000000000000000000651ef99cb9fcbe",
417
- dogecoin: "bip122:00000000001a91e3dace36e2be3bf030",
418
- litecoin: "bip122:12a765e31ffd4059bada1e25190f6e98",
419
- };
409
+ bitcoin: 'bip122:000000000019d6689c085ae165831e93',
410
+ bitcoinCash: 'bip122:000000000000000000651ef99cb9fcbe',
411
+ dogecoin: 'bip122:00000000001a91e3dace36e2be3bf030',
412
+ litecoin: 'bip122:12a765e31ffd4059bada1e25190f6e98',
413
+ }
420
414
 
421
415
  const COSMOS_CHAIN_IDS = {
422
- cosmos: "cosmos:cosmoshub-4",
423
- thorchain: "cosmos:thorchain-1",
424
- mayachain: "cosmos:mayachain-mainnet-v1",
425
- };
416
+ cosmos: 'cosmos:cosmoshub-4',
417
+ thorchain: 'cosmos:thorchain-1',
418
+ mayachain: 'cosmos:mayachain-mainnet-v1',
419
+ }
426
420
 
427
421
  const OTHER_CHAIN_IDS = {
428
- solana: "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
429
- };
422
+ solana: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',
423
+ }
424
+
425
+ // Listed in the asset selector, but swaps redirect to app.shapeshift.com (not executed in-widget)
426
+ const REDIRECT_ONLY_CHAIN_IDS = {
427
+ zcash: 'bip122:00040fe8ec8471911baa1db1266ea15d',
428
+ tron: 'tron:0x2b6653dc',
429
+ sui: 'sui:35834a8a',
430
+ ton: 'ton:mainnet',
431
+ near: 'near:mainnet',
432
+ starknet: 'starknet:SN_MAIN',
433
+ }
430
434
  ```
431
435
 
432
436
  ## Exported Hooks
433
437
 
434
438
  ```typescript
435
439
  import {
436
- useAssets,
437
440
  useAssetById,
438
- useChains,
439
- useAssetsByChainId,
440
441
  useAssetSearch,
441
- } from "@shapeshiftoss/swap-widget";
442
+ useAssets,
443
+ useAssetsByChainId,
444
+ useChains,
445
+ } from '@shapeshiftoss/swap-widget'
442
446
  ```
443
447
 
444
- | Hook | Return Type | Description |
445
- | --------------------------------- | ------------------------------------------ | -------------------------------------------------------------- |
446
- | `useAssets()` | `{ data: Asset[], isLoading, error, ... }` | Fetch all available assets. |
447
- | `useAssetById(assetId)` | `{ data: Asset \| undefined, ... }` | Fetch a specific asset by ID. |
448
- | `useChains()` | `{ data: ChainInfo[], ... }` | Fetch all available chains with their native assets. |
449
- | `useAssetsByChainId(chainId)` | `{ data: Asset[], ... }` | Fetch all assets for a specific chain. |
450
- | `useAssetSearch(query, chainId?)` | `{ data: Asset[], ... }` | Search assets by symbol or name, optionally filtered by chain. |
451
-
452
- ## Supported Chains
453
-
454
- | Chain | Chain ID | Type |
455
- | ----------------- | ----------------------------------------- | ------ |
456
- | Ethereum | `eip155:1` | EVM |
457
- | Arbitrum One | `eip155:42161` | EVM |
458
- | Optimism | `eip155:10` | EVM |
459
- | Polygon | `eip155:137` | EVM |
460
- | Base | `eip155:8453` | EVM |
461
- | Avalanche C-Chain | `eip155:43114` | EVM |
462
- | BNB Smart Chain | `eip155:56` | EVM |
463
- | Gnosis | `eip155:100` | EVM |
464
- | Bitcoin | `bip122:000000000019d6689c085ae165831e93` | UTXO |
465
- | Bitcoin Cash | `bip122:000000000000000000651ef99cb9fcbe` | UTXO |
466
- | Dogecoin | `bip122:00000000001a91e3dace36e2be3bf030` | UTXO |
467
- | Litecoin | `bip122:12a765e31ffd4059bada1e25190f6e98` | UTXO |
468
- | Cosmos Hub | `cosmos:cosmoshub-4` | Cosmos |
469
- | THORChain | `cosmos:thorchain-1` | Cosmos |
470
- | MAYAChain | `cosmos:mayachain-mainnet-v1` | Cosmos |
471
- | Solana | `solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp` | Solana |
472
-
473
- ## Notes and Limitations
474
-
475
- ### Multi-Chain Swap Support
448
+ | Hook | Description |
449
+ | --------------------------------- | -------------------------------------------------------------- |
450
+ | `useAssets()` | Fetch all available assets. |
451
+ | `useAssetById(assetId)` | Fetch a single asset by ID. |
452
+ | `useChains()` | Fetch all available chains with their native assets. |
453
+ | `useAssetsByChainId(chainId)` | Fetch all assets for a specific chain. |
454
+ | `useAssetSearch(query, chainId?)` | Search assets by symbol or name, optionally filtered by chain. |
476
455
 
477
- The widget supports swaps across multiple blockchain types:
456
+ These hooks must be used within a mounted `<SwapWidget />` tree (they rely on the widget's internal
457
+ React Query client).
478
458
 
479
- - **EVM swaps** (e.g., ETH to USDC, MATIC to WETH) can be executed directly within the widget when a wallet is connected via the `walletClient` prop or through the built-in AppKit wallet connection.
480
- - **Bitcoin/UTXO swaps** - When using the built-in wallet connection (`enableWalletConnection={true}`), Bitcoin and other UTXO chains can be signed directly via WalletConnect-compatible wallets.
481
- - **Solana swaps** - Solana transactions can be signed via Phantom, Solflare, or other Solana wallets when using the built-in wallet connection.
482
- - **Unsupported chains** - Swaps involving chains without wallet support will redirect to [app.shapeshift.com](https://app.shapeshift.com) to complete the transaction.
483
-
484
- ### Partner Codes
459
+ ## Supported Chains
485
460
 
486
- Register an affiliate account at the affiliate dashboard and claim a partner code (e.g. `vultisig`, `venice`). Pass it via the `partnerCode` prop on the widget, or the `X-Partner-Code` header when using the REST API directly:
461
+ Assets on the following chains appear in the selector. Swaps are **executed in-widget** only for EVM,
462
+ UTXO, and Solana assets (`isWidgetExecutableChainId` returns `true`). Cosmos-SDK and redirect-only
463
+ chains are selectable but route the user to [app.shapeshift.com](https://app.shapeshift.com) to
464
+ complete the swap (when `allowShapeshiftRedirect` is enabled).
465
+
466
+ | Chain | Chain ID | Type | Executable in-widget |
467
+ | ----------------- | ----------------------------------------- | ------ | -------------------- |
468
+ | Ethereum | `eip155:1` | EVM | ✅ |
469
+ | Arbitrum One | `eip155:42161` | EVM | ✅ |
470
+ | Optimism | `eip155:10` | EVM | ✅ |
471
+ | Polygon | `eip155:137` | EVM | ✅ |
472
+ | Base | `eip155:8453` | EVM | ✅ |
473
+ | Avalanche C-Chain | `eip155:43114` | EVM | ✅ |
474
+ | BNB Smart Chain | `eip155:56` | EVM | ✅ |
475
+ | Gnosis | `eip155:100` | EVM | ✅ |
476
+ | Monad | `eip155:143` | EVM | ✅ |
477
+ | MegaETH | `eip155:4326` | EVM | ✅ |
478
+ | HyperEVM | `eip155:999` | EVM | ✅ |
479
+ | Plasma | `eip155:9745` | EVM | ✅ |
480
+ | Katana | `eip155:747474` | EVM | ✅ |
481
+ | Bitcoin | `bip122:000000000019d6689c085ae165831e93` | UTXO | ✅ |
482
+ | Bitcoin Cash | `bip122:000000000000000000651ef99cb9fcbe` | UTXO | ✅ |
483
+ | Dogecoin | `bip122:00000000001a91e3dace36e2be3bf030` | UTXO | ✅ |
484
+ | Litecoin | `bip122:12a765e31ffd4059bada1e25190f6e98` | UTXO | ✅ |
485
+ | Solana | `solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp` | Solana | ✅ |
486
+ | Cosmos Hub | `cosmos:cosmoshub-4` | Cosmos | ↗ redirect |
487
+ | THORChain | `cosmos:thorchain-1` | Cosmos | ↗ redirect |
488
+ | MAYAChain | `cosmos:mayachain-mainnet-v1` | Cosmos | ↗ redirect |
489
+ | Zcash, Tron, Sui, TON, NEAR, Starknet | _see `REDIRECT_ONLY_CHAIN_IDS`_ | Other | ↗ redirect |
490
+
491
+ ## Supported Swappers
492
+
493
+ The widget aggregates quotes across the protocols below and surfaces the best rate. Use
494
+ `allowedSwapperNames` to restrict which are used.
495
+
496
+ - **NEAR Intents** (`SwapperName.NearIntents`)
497
+ - **Relay** (`SwapperName.Relay`)
498
+ - **THORChain** (`SwapperName.Thorchain`)
499
+ - **MAYAChain** (`SwapperName.Mayachain`)
500
+
501
+ > The set of enabled swappers changes over time. Treat this list as current-at-publish; the
502
+ > authoritative source is the `SwapperName` enum exported by this package.
503
+
504
+ ## Partner Codes & Affiliate Revenue
505
+
506
+ Pass your registered `partnerCode` to attribute swaps to your affiliate account and earn revenue
507
+ share. The widget forwards it to the ShapeShift Public API as the `X-Partner-Code` header, and the
508
+ API applies your configured fee automatically.
487
509
 
510
+ ```tsx
511
+ <SwapWidget walletConnectProjectId="..." partnerCode="your-partner-code" />
488
512
  ```
489
- GET /v1/swap/rates?...
490
- X-Partner-Code: your-partner-code
491
- ```
492
-
493
- The API resolves the partner code to the registered affiliate address and BPS automatically.
494
513
 
495
- ### Internal QueryClient
514
+ See the [Affiliate Program guide](../../docs/affiliates.md) for how to obtain a partner code and how
515
+ revenue attribution works.
496
516
 
497
- The widget manages its own React Query `QueryClient` internally. You do not need to wrap it in a `QueryClientProvider`.
498
-
499
- ### Swap Aggregation
500
-
501
- The widget fetches quotes from multiple DEXs and aggregators including:
502
-
503
- - THORChain
504
- - MAYAChain
505
- - CoW Swap
506
- - 0x
507
- - 1inch
508
- - Portals
509
- - Chainflip
510
- - Jupiter (Solana)
511
- - Bebop
512
- - Relay
513
- - ButterSwap
514
- - Arbitrum Bridge
515
-
516
- ### Wallet Balance Display
517
-
518
- When a wallet is connected (`walletClient` prop), the widget displays the user's balance for the selected sell and buy assets. This only works for EVM chains where the connected wallet has assets.
519
-
520
- ### USD Price Display
521
-
522
- The widget automatically fetches and displays USD prices for selected assets.
523
-
524
- ### Mobile Responsive
517
+ ## Notes and Limitations
525
518
 
526
- The widget is designed to be responsive and works well on mobile devices.
519
+ - **Self-contained providers.** The widget renders its own `WagmiProvider` and React Query
520
+ `QueryClient`. Don't wrap it in your own — and remember it renders nothing until AppKit is
521
+ initialized, whether by `walletConnectProjectId` or by your host app (see [Wallet Connection](#wallet-connection)).
522
+ - **Balances and USD prices.** When a wallet is connected, the widget shows balances and USD prices
523
+ for the selected assets.
524
+ - **Redirects.** Assets on non-executable chains (Cosmos, Zcash, Tron, Sui, TON, NEAR, Starknet)
525
+ send the user to app.shapeshift.com to finish the swap, unless `allowShapeshiftRedirect={false}`.
526
+ - **Mobile responsive.** The widget is designed to work on mobile as well as desktop.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shapeshiftoss/swap-widget",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "description": "Embeddable swap widget using ShapeShift API",
5
5
  "repository": "https://github.com/shapeshift/web",
6
6
  "license": "MIT",
@@ -30,9 +30,9 @@
30
30
  "p-queue": "^8.0.1",
31
31
  "react-virtuoso": "^4.7.11",
32
32
  "xstate": "5.28.0",
33
- "@shapeshiftoss/types": "^8.6.7",
34
- "@shapeshiftoss/caip": "^8.16.7",
35
- "@shapeshiftoss/utils": "^1.0.5"
33
+ "@shapeshiftoss/utils": "^1.1.0",
34
+ "@shapeshiftoss/types": "^8.6.8",
35
+ "@shapeshiftoss/caip": "^8.16.9"
36
36
  },
37
37
  "peerDependencies": {
38
38
  "@wagmi/core": "^2.0.0",
@@ -91,7 +91,6 @@
91
91
  "viem": "2.43.5",
92
92
  "vite": "^5.0.0",
93
93
  "vite-plugin-node-polyfills": "0.23.0",
94
- "vitest": "4.0.18",
95
94
  "wagmi": "2.19.5"
96
95
  },
97
96
  "publishConfig": {