@de1/widget
v1.0.3
Published
De¹ Exchange Widget for cross-chain bridging and swapping. It will drive your multi-chain strategy and attract new users from everywhere.
Readme
De¹ Widget
De¹ Exchange 跨链兑换与桥接组件。把 Swap / Bridge 能力嵌入任意 Web 应用,支持 EVM、Solana、Bitcoin、NEAR 等多链钱包。
- 产品站点:de1.exchange
- 接入文档:docs.de1.exchange/docs/widget/overview
- 在线演示:widget.de1.exchange
- 技术支持:t.me/De1_Exchange
能力概览
- 同链兑换(Swap) 与 跨链桥接(Bridge),路由聚合报价
- 开箱即用 UI:代币选择、钱包连接、交易进度、历史记录
- 多链钱包:MetaMask、WalletConnect、Coinbase、Solana、UTXO、NEAR
- 可定制:主题、布局、语言、链/代币白名单、手续费与推荐码
- 可观测:交易生命周期事件,便于埋点与风控
- 多框架示例:React、Next.js、Vue、Nuxt、Svelte、Remix,以及 RainbowKit / Privy / Dynamic 等钱包栈
包说明
本仓库是 pnpm monorepo,对外主要使用以下 npm 包:
| 包名 | 说明 |
| --- | --- |
| @de1/widget | React Widget 组件,接入首选 |
| @de1/widget-sdk | 报价、余额、路由执行等无头 SDK |
| @de1/wallet-management | 钱包连接与账户管理 |
| @de1/widget-types | 共享 TypeScript 类型 |
当前 Widget 版本:@de1/[email protected]。
快速接入
1. 安装
npm install @de1/widget
# 或
pnpm add @de1/widgetPeer 依赖(宿主项目需要提供):
npm install react react-dom @tanstack/react-query wagmi viem @bigmi/react @solana/wallet-adapter-react| 依赖 | 版本要求 |
| --- | --- |
| react / react-dom | >= 18 |
| wagmi | ^2.14.0 |
| @tanstack/react-query | ^5.62.0 |
| viem | ^2 |
| @bigmi/react | >= 0.1.0 |
| @solana/wallet-adapter-react | ^0.15.35 |
Vite 项目还需 Node polyfill:
npm install -D vite-plugin-node-polyfills// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { nodePolyfills } from 'vite-plugin-node-polyfills'
export default defineConfig({
plugins: [
react(),
nodePolyfills({ include: ['buffer', 'process'] }),
],
})2. 渲染 Widget
integrator 必填,用于标识接入方。请使用稳定的英文标识,例如产品名或域名。
import { De1Widget } from '@de1/widget'
export function App() {
return (
<De1Widget
integrator="your-app-name"
config={{
variant: 'compact',
appearance: 'auto',
theme: {
container: {
border: '1px solid rgb(234, 234, 234)',
borderRadius: '16px',
},
},
}}
/>
)
}Next.js App Router 必须在客户端渲染,可用 WidgetSkeleton 作为 SSR 占位:
'use client'
import { De1Widget, WidgetSkeleton } from '@de1/widget'
import { useEffect, useState } from 'react'
export function Widget() {
const [mounted, setMounted] = useState(false)
useEffect(() => setMounted(true), [])
const config = {
appearance: 'light' as const,
theme: { container: { borderRadius: '16px' } },
}
if (!mounted) {
return <WidgetSkeleton config={config} />
}
return <De1Widget integrator="your-app-name" config={config} />
}next.config.js 中加入:
module.exports = {
transpilePackages: ['@de1/widget'],
}常用配置
De1Widget 同时接受顶层 props 和 config 对象,二者会合并。完整类型见 WidgetConfig。
布局与模式
| 字段 | 取值 | 说明 |
| --- | --- | --- |
| variant | 'compact' | 'wide' | 'drawer' | 卡片 / 宽屏 / 抽屉 |
| subvariant | 'default' | 'swap' | 'bridge' | 'split' | 'custom' | 'refuel' | 业务模式 |
| appearance | 'light' | 'dark' | 'auto' | 明暗主题 |
| buildUrl | boolean | 是否把表单状态同步到 URL |
只做兑换或只做跨链:
<De1Widget integrator="your-app-name" config={{ subvariant: 'swap' }} />
<De1Widget integrator="your-app-name" config={{ subvariant: 'bridge' }} />抽屉模式可通过 ref 控制开关:
import { useRef } from 'react'
import { De1Widget, type WidgetDrawer } from '@de1/widget'
export function SwapDrawer() {
const drawerRef = useRef<WidgetDrawer>(null)
return (
<>
<button onClick={() => drawerRef.current?.openDrawer()}>Open</button>
<De1Widget
ref={drawerRef}
integrator="your-app-name"
variant="drawer"
/>
</>
)
}默认交易对
<De1Widget
integrator="your-app-name"
config={{
fromChain: 1,
fromToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC
toChain: 56,
toToken: '0x0000000000000000000000000000000000000000', // BNB
fromAmount: '100',
}}
/>链与代币过滤
import { ChainType } from '@de1/widget'
<De1Widget
integrator="your-app-name"
config={{
chains: {
allow: [1, 56, 137, 42161],
types: { allow: [ChainType.EVM] },
},
tokens: {
deny: [{ chainId: 1, address: '0x...' }],
},
}}
/>隐藏 / 禁用 UI
import { DisabledUI, HiddenUI, RequiredUI } from '@de1/widget'
<De1Widget
integrator="your-app-name"
config={{
hiddenUI: [HiddenUI.Language, HiddenUI.Appearance, HiddenUI.PoweredBy],
disabledUI: [DisabledUI.ToAddress],
requiredUI: [RequiredUI.ToAddress],
}}
/>主题
可直接使用内置 de1Theme,或按品牌覆盖色板:
import { De1Widget, de1Theme } from '@de1/widget'
<De1Widget
integrator="your-app-name"
config={{
appearance: 'dark',
theme: {
...de1Theme,
palette: {
...de1Theme.palette,
primary: { main: '#fb534f' },
},
container: {
borderRadius: '16px',
boxShadow: '0px 8px 32px rgba(0, 0, 0, 0.08)',
},
},
}}
/>语言
内置:en、zh、ja、ko、es、fr、de、pt、vi、th、id、tr、it、uk、bn。
<De1Widget
integrator="your-app-name"
config={{
languages: { default: 'zh' },
}}
/>可用 languageResources 覆盖指定文案。
手续费与推荐码
<De1Widget
integrator="your-app-name"
config={{
fee: 0.003, // 0.3%
evmReferrer: {
address: '0xYourFeeReceiver',
fee: '0.003',
},
solanaReferrer: {
address: 'YourSolanaAddress',
fee: '0.003',
},
}}
/>动态费率使用 feeConfig.calculateFee。
事件
用 useWidgetEvents 订阅交易与表单事件,适合埋点和客服跳转。
import { useEffect } from 'react'
import { useWidgetEvents, WidgetEvent } from '@de1/widget'
export function WidgetAnalytics() {
const events = useWidgetEvents()
useEffect(() => {
const onStarted = (route) => console.log('started', route.id)
const onCompleted = (route) => console.log('completed', route.id)
const onFailed = ({ route, process }) =>
console.error('failed', route.id, process)
events.on(WidgetEvent.RouteExecutionStarted, onStarted)
events.on(WidgetEvent.RouteExecutionCompleted, onCompleted)
events.on(WidgetEvent.RouteExecutionFailed, onFailed)
return () => {
events.off(WidgetEvent.RouteExecutionStarted, onStarted)
events.off(WidgetEvent.RouteExecutionCompleted, onCompleted)
events.off(WidgetEvent.RouteExecutionFailed, onFailed)
}
}, [events])
return null
}常用事件:
| 事件 | 时机 |
| --- | --- |
| RouteExecutionStarted | 用户确认并开始执行 |
| RouteExecutionUpdated | 步骤状态更新 |
| RouteExecutionCompleted | 成功 |
| RouteExecutionFailed | 失败 |
| RouteSelected | 用户选择某条路由 |
| AvailableRoutes | 报价列表就绪 |
| WalletConnected | 钱包已连接 |
| SourceChainTokenSelected / DestinationChainTokenSelected | 代币变更 |
| ContactSupport | 用户点击联系支持 |
| PageEntered | 页面切换 |
钱包接入
Widget 自带钱包菜单。若站点已有 RainbowKit、Privy、Dynamic、Reown 等方案,通过 walletConfig.onConnect 把「连接钱包」交给宿主:
<De1Widget
integrator="your-app-name"
config={{
walletConfig: {
onConnect() {
openConnectModal?.()
},
},
}}
/>需要 Widget 与外部钱包菜单并存时,开启 usePartialWalletManagement。
WalletConnect / MetaMask / Coinbase 可在 walletConfig 中传入各自参数(例如 WalletConnect projectId)。
更完整的钱包栈示例见 examples/。
无头 SDK
不需要 UI、只做报价或执行路由时使用 @de1/widget-sdk:
import { createConfig, getRoutes, executeRoute } from '@de1/widget-sdk'
createConfig({ integrator: 'your-app-name' })
const { routes } = await getRoutes({
fromChainId: 1,
toChainId: 56,
fromTokenAddress: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
toTokenAddress: '0x0000000000000000000000000000000000000000',
fromAmount: '100000000',
})
if (routes?.[0]) {
await executeRoute(routes[0])
}常用方法:getRoutes、getQuote、getStatus、getTokens、getChains、getTokenBalances、executeRoute、resumeRoute、stopRouteExecution。
框架示例
逐步说明在 examples/README.md。目录对照:
| 目录 | 场景 |
| --- | --- |
| examples/vite | Vite + React |
| examples/nextjs | Next.js App Router |
| examples/nextjs-page-router | Next.js Pages Router |
| examples/vue / examples/nuxt | Vue 3 / Nuxt 3(veaury) |
| examples/svelte | Svelte |
| examples/remix | Remix |
| examples/rainbowkit | RainbowKit |
| examples/privy | Privy |
| examples/dynamic | Dynamic |
| examples/reown | Reown (WalletConnect) |
| examples/deposit-flow | 自定义充值 / 合约调用 |
本地开发
pnpm install
pnpm dev # Widget Playground(Vite)
pnpm build # 构建全部 packages
pnpm check # Biome
pnpm check:types # TypeScript要求:Node.js 18+,包管理器 pnpm@10。
仓库结构:
packages/
widget/ # @de1/widget
widget-sdk/ # @de1/widget-sdk
wallet-management/ # @de1/wallet-management
widget-types/ # @de1/widget-types
widget-playground-vite/ # 本地调试台
examples/ # 各框架接入示例接入注意
integrator必填。未设置时 SDK 会拒绝初始化。- Widget 依赖浏览器钱包与 Web3 API,SSR 框架请包一层 Client-only。
- Vite / 部分打包器需要
buffer、processpolyfill。 - 合约 ABI、Permit2 类型名等链上字段请勿改写,以免破坏签名与执行。
- 生产环境请自行配置 WalletConnect Project ID,以及所需的 RPC。
支持
- 开发者文档:https://docs.de1.exchange/docs/widget/overview
- Telegram:https://t.me/De1_Exchange
- 问题反馈:请在本仓库提交 Issue,并附上
integrator、复现步骤与浏览器控制台信息
License
Apache-2.0,见 LICENSE.md。
