orderly-sdk-dex-architecture
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseOrderly Network: SDK DEX Architecture
Orderly Network:SDK DEX架构
A comprehensive guide to architecting and scaffolding a complete DEX application using the Orderly Network Components SDK.
这是一份使用Orderly Network Components SDK构建完整DEX应用的架构与脚手架搭建综合指南。
When to Use
适用场景
- Setting up a new DEX project
- Understanding the provider hierarchy
- Configuring network settings
- Setting up TradingView charts
- Understanding runtime configuration
- 搭建新的DEX项目
- 理解提供者层级结构
- 配置网络设置
- 搭建TradingView图表
- 理解运行时配置
Prerequisites
前置条件
- Node.js 18+ installed
- Orderly SDK packages installed (see )
orderly-sdk-install-dependency - React 18+ project with TypeScript
- Vite or similar build tool
- 已安装Node.js 18+
- 已安装Orderly SDK包(参考)
orderly-sdk-install-dependency - 基于React 18+的TypeScript项目
- Vite或类似构建工具
Overview
概述
This skill covers the complete architecture for building a production-ready DEX:
- Project structure and setup
- Provider hierarchy and configuration
- Network configuration (REQUIRED) - mainnet/testnet and supported chains
- TradingView chart setup (REQUIRED for charts) - charting library files
- Routing and page components
- Runtime configuration
- Build and deployment
Critical Configuration: Every DEX must have:
- - Your Orderly broker ID
brokerId - - Either "mainnet" or "testnet"
networkId - Proper wallet connector setup with matching network
- TradingView charting library in (for chart functionality)
public/tradingview/
本指南涵盖构建生产级DEX的完整架构:
- 项目结构与搭建
- 提供者层级与配置
- 网络配置(必填) - 主网/测试网及支持的链
- TradingView图表搭建(图表功能必填) - 图表库文件
- 路由与页面组件
- 运行时配置
- 构建与部署
关键配置:每个DEX必须具备:
- - 你的Orderly经纪商ID
brokerId - - 取值为"mainnet"或"testnet"
networkId - 与网络匹配的钱包连接器配置
- 放置在中的TradingView图表库(用于图表功能)
public/tradingview/
Project Structure
项目结构
my-dex/
├── public/
│ ├── config.js # Runtime configuration
│ ├── favicon.webp
│ ├── locales/ # i18n translations
│ │ ├── en.json
│ │ └── extend/ # Custom translations
│ ├── pnl/ # PnL share poster backgrounds
│ │ ├── poster_bg_1.png
│ │ └── poster_bg_2.png
│ └── tradingview/ # TradingView library (REQUIRED for charts)
│ ├── chart.css # Custom chart styles
│ └── charting_library/ # TradingView charting library files
├── src/
│ ├── main.tsx # Entry point
│ ├── App.tsx # Root component with router
│ ├── components/
│ │ ├── orderlyProvider/ # SDK provider setup
│ │ │ ├── index.tsx # Main provider wrapper
│ │ │ └── walletConnector.tsx
│ │ ├── ErrorBoundary.tsx
│ │ └── LoadingSpinner.tsx
│ ├── pages/
│ │ ├── perp/ # Trading pages
│ │ ├── portfolio/ # Portfolio pages
│ │ ├── markets/ # Markets pages
│ │ └── leaderboard/ # Leaderboard pages
│ ├── utils/
│ │ ├── config.tsx # App configuration
│ │ ├── walletConfig.ts # Wallet setup
│ │ ├── runtime-config.ts # Runtime config loader
│ │ └── storage.ts # Local storage utils
│ └── styles/
│ └── index.css # Global styles + Tailwind
├── .env # Build-time env vars
├── index.html
├── package.json
├── tailwind.config.ts
├── tsconfig.json
└── vite.config.tsmy-dex/
├── public/
│ ├── config.js # 运行时配置
│ ├── favicon.webp
│ ├── locales/ # 国际化翻译文件
│ │ ├── en.json
│ │ └── extend/ # 自定义翻译内容
│ ├── pnl/ # PnL分享海报背景图
│ │ ├── poster_bg_1.png
│ │ └── poster_bg_2.png
│ └── tradingview/ # TradingView库(图表功能必填)
│ ├── chart.css # 自定义图表样式
│ └── charting_library/ # TradingView图表库文件
├── src/
│ ├── main.tsx # 入口文件
│ ├── App.tsx # 带路由的根组件
│ ├── components/
│ │ ├── orderlyProvider/ # SDK提供者配置
│ │ │ ├── index.tsx # 主提供者包装器
│ │ │ └── walletConnector.tsx
│ │ ├── ErrorBoundary.tsx
│ │ └── LoadingSpinner.tsx
│ ├── pages/
│ │ ├── perp/ # 交易页面
│ │ ├── portfolio/ # 资产组合页面
│ │ ├── markets/ # 市场页面
│ │ └── leaderboard/ # 排行榜页面
│ ├── utils/
│ │ ├── config.tsx # 应用配置
│ │ ├── walletConfig.ts # 钱包设置
│ │ ├── runtime-config.ts # 运行时配置加载器
│ │ └── storage.ts # 本地存储工具
│ └── styles/
│ └── index.css # 全局样式 + Tailwind
├── .env # 构建时环境变量
├── index.html
├── package.json
├── tailwind.config.ts
├── tsconfig.json
└── vite.config.tsProvider Hierarchy
提供者层级
The SDK requires a specific provider nesting order:
LocaleProvider (i18n)
└── WalletConnectorProvider (or Privy)
└── OrderlyAppProvider
├── (internal) AppConfigProvider
├── (internal) OrderlyThemeProvider
├── (internal) OrderlyConfigProvider (from hooks)
├── (internal) AppStateProvider
├── (internal) UILocaleProvider
├── (internal) TooltipProvider
├── (internal) ModalProvider
└── Your AppNote:andTooltipProviderare managed internally byModalProvider. You do not need to add them yourself.OrderlyAppProvider
SDK要求特定的提供者嵌套顺序:
LocaleProvider (国际化)
└── WalletConnectorProvider (或Privy)
└── OrderlyAppProvider
├── (内部) AppConfigProvider
├── (内部) OrderlyThemeProvider
├── (内部) OrderlyConfigProvider (来自hooks)
├── (内部) AppStateProvider
├── (内部) UILocaleProvider
├── (内部) TooltipProvider
├── (内部) ModalProvider
└── 你的应用注意:和TooltipProvider由ModalProvider内部管理,你无需自行添加。OrderlyAppProvider
Main Provider Component
主提供者组件
tsx
// src/components/orderlyProvider/index.tsx
import { ReactNode, useCallback, Suspense, lazy } from 'react';
import { OrderlyAppProvider } from '@orderly.network/react-app';
import { LocaleProvider, LocaleCode, defaultLanguages } from '@orderly.network/i18n';
import type { NetworkId } from '@orderly.network/types';
import { useOrderlyConfig } from '@/utils/config';
import { getRuntimeConfig, getRuntimeConfigBoolean } from '@/utils/runtime-config';
const NETWORK_ID_KEY = 'orderly_network_id';
const getNetworkId = (): NetworkId => {
if (typeof window === 'undefined') return 'mainnet';
const disableMainnet = getRuntimeConfigBoolean('VITE_DISABLE_MAINNET');
const disableTestnet = getRuntimeConfigBoolean('VITE_DISABLE_TESTNET');
if (disableMainnet && !disableTestnet) return 'testnet';
if (disableTestnet && !disableMainnet) return 'mainnet';
return (localStorage.getItem(NETWORK_ID_KEY) as NetworkId) || 'mainnet';
};
const WalletConnector = lazy(() => import('./walletConnector'));
const OrderlyProvider = ({ children }: { children: ReactNode }) => {
const config = useOrderlyConfig();
const networkId = getNetworkId();
const onChainChanged = useCallback((_chainId: number, { isTestnet }: { isTestnet: boolean }) => {
const currentNetworkId = getNetworkId();
if (
(isTestnet && currentNetworkId === 'mainnet') ||
(!isTestnet && currentNetworkId === 'testnet')
) {
const newNetworkId: NetworkId = isTestnet ? 'testnet' : 'mainnet';
localStorage.setItem(NETWORK_ID_KEY, newNetworkId);
window.location.reload();
}
}, []);
const onLanguageChanged = (lang: LocaleCode) => {
const url = new URL(window.location.href);
if (lang === 'en') {
url.searchParams.delete('lang');
} else {
url.searchParams.set('lang', lang);
}
window.history.replaceState({}, '', url.toString());
};
return (
<LocaleProvider onLanguageChanged={onLanguageChanged} locale="en" languages={defaultLanguages}>
<Suspense fallback={<LoadingSpinner />}>
<WalletConnector networkId={networkId}>
<OrderlyAppProvider
brokerId={getRuntimeConfig('VITE_ORDERLY_BROKER_ID')}
brokerName={getRuntimeConfig('VITE_ORDERLY_BROKER_NAME')}
networkId={networkId}
onChainChanged={onChainChanged}
appIcons={config.appIcons}
>
{children}
</OrderlyAppProvider>
</WalletConnector>
</Suspense>
</LocaleProvider>
);
};
export default OrderlyProvider;tsx
// src/components/orderlyProvider/index.tsx
import { ReactNode, useCallback, Suspense, lazy } from 'react';
import { OrderlyAppProvider } from '@orderly.network/react-app';
import { LocaleProvider, LocaleCode, defaultLanguages } from '@orderly.network/i18n';
import type { NetworkId } from '@orderly.network/types';
import { useOrderlyConfig } from '@/utils/config';
import { getRuntimeConfig, getRuntimeConfigBoolean } from '@/utils/runtime-config';
const NETWORK_ID_KEY = 'orderly_network_id';
const getNetworkId = (): NetworkId => {
if (typeof window === 'undefined') return 'mainnet';
const disableMainnet = getRuntimeConfigBoolean('VITE_DISABLE_MAINNET');
const disableTestnet = getRuntimeConfigBoolean('VITE_DISABLE_TESTNET');
if (disableMainnet && !disableTestnet) return 'testnet';
if (disableTestnet && !disableMainnet) return 'mainnet';
return (localStorage.getItem(NETWORK_ID_KEY) as NetworkId) || 'mainnet';
};
const WalletConnector = lazy(() => import('./walletConnector'));
const OrderlyProvider = ({ children }: { children: ReactNode }) => {
const config = useOrderlyConfig();
const networkId = getNetworkId();
const onChainChanged = useCallback((_chainId: number, { isTestnet }: { isTestnet: boolean }) => {
const currentNetworkId = getNetworkId();
if (
(isTestnet && currentNetworkId === 'mainnet') ||
(!isTestnet && currentNetworkId === 'testnet')
) {
const newNetworkId: NetworkId = isTestnet ? 'testnet' : 'mainnet';
localStorage.setItem(NETWORK_ID_KEY, newNetworkId);
window.location.reload();
}
}, []);
const onLanguageChanged = (lang: LocaleCode) => {
const url = new URL(window.location.href);
if (lang === 'en') {
url.searchParams.delete('lang');
} else {
url.searchParams.set('lang', lang);
}
window.history.replaceState({}, '', url.toString());
};
return (
<LocaleProvider onLanguageChanged={onLanguageChanged} locale="en" languages={defaultLanguages}>
<Suspense fallback={<LoadingSpinner />}>
<WalletConnector networkId={networkId}>
<OrderlyAppProvider
brokerId={getRuntimeConfig('VITE_ORDERLY_BROKER_ID')}
brokerName={getRuntimeConfig('VITE_ORDERLY_BROKER_NAME')}
networkId={networkId}
onChainChanged={onChainChanged}
appIcons={config.appIcons}
>
{children}
</OrderlyAppProvider>
</WalletConnector>
</Suspense>
</LocaleProvider>
);
};
export default OrderlyProvider;Wallet Connector Setup
钱包连接器设置
Note: BothandsolanaInitialprops onevmInitialare optional. The provider has sensible defaults and the official templates use it with no props. Pass these props only if you need to customize wallet configuration.WalletConnectorProvider
Minimal Setup (recommended — uses defaults):
tsx
// src/components/orderlyProvider/walletConnector.tsx
import { ReactNode } from 'react';
import { WalletConnectorProvider } from '@orderly.network/wallet-connector';
import type { NetworkId } from '@orderly.network/types';
interface Props {
children: ReactNode;
networkId: NetworkId;
}
const WalletConnector = ({ children, networkId }: Props) => {
return <WalletConnectorProvider>{children}</WalletConnectorProvider>;
};
export default WalletConnector;注意:上的WalletConnectorProvider和solanaInitial属性均为可选。该提供者有合理的默认值,官方模板使用时无需传入属性。仅当你需要自定义钱包配置时才传入这些属性。evmInitial
最简设置(推荐——使用默认值):
tsx
// src/components/orderlyProvider/walletConnector.tsx
import { ReactNode } from 'react';
import { WalletConnectorProvider } from '@orderly.network/wallet-connector';
import type { NetworkId } from '@orderly.network/types';
interface Props {
children: ReactNode;
networkId: NetworkId;
}
const WalletConnector = ({ children, networkId }: Props) => {
return <WalletConnectorProvider>{children}</WalletConnectorProvider>;
};
export default WalletConnector;Network Configuration (REQUIRED)
网络配置(必填)
IMPORTANT: Every Orderly DEX must configure the network properly.
重要提示:每个Orderly DEX必须正确配置网络。
Supported Networks
支持的网络
Mainnet Chains (Production)
| Chain | Chain ID | Description |
|---|---|---|
| Arbitrum | 42161 | Primary mainnet chain |
| Optimism | 10 | OP mainnet |
| Base | 8453 | Base mainnet |
| Ethereum | 1 | Ethereum mainnet |
| Solana | N/A | Solana mainnet |
Testnet Chains (Development)
| Chain | Chain ID | Description |
|---|---|---|
| Arbitrum Sepolia | 421614 | Primary testnet chain |
| Base Sepolia | 84532 | Base testnet |
| Solana Devnet | 901901901 | Solana devnet |
主网链(生产环境)
| 链名称 | 链ID | 描述 |
|---|---|---|
| Arbitrum | 42161 | 主要主网链 |
| Optimism | 10 | OP主网 |
| Base | 8453 | Base主网 |
| Ethereum | 1 | Ethereum主网 |
| Solana | N/A | Solana主网 |
测试网链(开发环境)
| 链名称 | 链ID | 描述 |
|---|---|---|
| Arbitrum Sepolia | 421614 | 主要测试网链 |
| Base Sepolia | 84532 | Base测试网 |
| Solana Devnet | 901901901 | Solana开发网 |
Network ID Configuration
网络ID配置
The prop determines whether your DEX connects to mainnet or testnet.
networkIdtsx
import type { NetworkId } from '@orderly.network/types';
// Network ID must be "mainnet" or "testnet"
const networkId: NetworkId = 'mainnet'; // or "testnet"networkIdtsx
import type { NetworkId } from '@orderly.network/types';
// 网络ID必须为"mainnet"或"testnet"
const networkId: NetworkId = 'mainnet'; // 或"testnet"Complete Provider Setup with Network Config
带网络配置的完整提供者设置
tsx
<WalletConnectorProvider
solanaInitial={{
network: networkId === "mainnet"
? WalletAdapterNetwork.Mainnet
: WalletAdapterNetwork.Devnet,
wallets: [],
}}
evmInitial={{
options: {
wallets: [],
},
}}
>
<OrderlyAppProvider
brokerId="your_broker_id"
brokerName="Your DEX Name"
networkId={networkId}
onChainChanged={onChainChanged}
>tsx
<WalletConnectorProvider
solanaInitial={{
network: networkId === "mainnet"
? WalletAdapterNetwork.Mainnet
: WalletAdapterNetwork.Devnet,
wallets: [],
}}
evmInitial={{
options: {
wallets: [],
},
}}
>
<OrderlyAppProvider
brokerId="your_broker_id"
brokerName="Your DEX Name"
networkId={networkId}
onChainChanged={onChainChanged}
>Runtime Configuration
运行时配置
Use runtime configuration for deployment flexibility:
使用运行时配置提升部署灵活性:
public/config.js
public/config.js
javascript
window.__RUNTIME_CONFIG__ = {
VITE_ORDERLY_BROKER_ID: 'your_broker_id',
VITE_ORDERLY_BROKER_NAME: 'Your DEX Name',
VITE_DISABLE_MAINNET: 'false',
VITE_DISABLE_TESTNET: 'false',
VITE_DEFAULT_CHAIN: '42161',
};javascript
window.__RUNTIME_CONFIG__ = {
VITE_ORDERLY_BROKER_ID: 'your_broker_id',
VITE_ORDERLY_BROKER_NAME: 'Your DEX Name',
VITE_DISABLE_MAINNET: 'false',
VITE_DISABLE_TESTNET: 'false',
VITE_DEFAULT_CHAIN: '42161',
};Runtime Config Loader
运行时配置加载器
tsx
// src/utils/runtime-config.ts
export function getRuntimeConfig(key: string): string {
if (typeof window !== 'undefined' && window.__RUNTIME_CONFIG__?.[key]) {
return window.__RUNTIME_CONFIG__[key];
}
return import.meta.env[key] || '';
}
export function getRuntimeConfigBoolean(key: string): boolean {
return getRuntimeConfig(key) === 'true';
}
export function getRuntimeConfigArray(key: string): string[] {
const value = getRuntimeConfig(key);
if (!value) return [];
return value
.split(',')
.map((s) => s.trim())
.filter(Boolean);
}
declare global {
interface Window {
__RUNTIME_CONFIG__?: Record<string, string>;
}
}tsx
// src/utils/runtime-config.ts
export function getRuntimeConfig(key: string): string {
if (typeof window !== 'undefined' && window.__RUNTIME_CONFIG__?.[key]) {
return window.__RUNTIME_CONFIG__[key];
}
return import.meta.env[key] || '';
}
export function getRuntimeConfigBoolean(key: string): boolean {
return getRuntimeConfig(key) === 'true';
}
export function getRuntimeConfigArray(key: string): string[] {
const value = getRuntimeConfig(key);
if (!value) return [];
return value
.split(',')
.map((s) => s.trim())
.filter(Boolean);
}
declare global {
interface Window {
__RUNTIME_CONFIG__?: Record<string, string>;
}
}App Root Component
应用根组件
tsx
// src/App.tsx
import { Outlet } from 'react-router-dom';
import { Suspense } from 'react';
import OrderlyProvider from '@/components/orderlyProvider';
import { LoadingSpinner } from '@/components/LoadingSpinner';
import { ErrorBoundary } from '@/components/ErrorBoundary';
export default function App() {
return (
<ErrorBoundary>
<OrderlyProvider>
<Suspense fallback={<LoadingSpinner />}>
<Outlet />
</Suspense>
</OrderlyProvider>
</ErrorBoundary>
);
}tsx
// src/App.tsx
import { Outlet } from 'react-router-dom';
import { Suspense } from 'react';
import OrderlyProvider from '@/components/orderlyProvider';
import { LoadingSpinner } from '@/components/LoadingSpinner';
import { ErrorBoundary } from '@/components/ErrorBoundary';
export default function App() {
return (
<ErrorBoundary>
<OrderlyProvider>
<Suspense fallback={<LoadingSpinner />}>
<Outlet />
</Suspense>
</OrderlyProvider>
</ErrorBoundary>
);
}TradingView Chart Setup (REQUIRED)
TradingView图表设置(必填)
CRITICAL: The TradingView charting library must be manually added to yourfolder.public/tradingview/
关键提示:TradingView图表库必须手动添加到你的文件夹中。public/tradingview/
Required Files Structure
必填文件结构
public/
└── tradingview/
├── chart.css # Optional: custom chart styling
└── charting_library/ # REQUIRED: TradingView library
├── charting_library.js # Main library script
├── charting_library.d.ts
└── ... (other library files)public/
└── tradingview/
├── chart.css # 可选:自定义图表样式
└── charting_library/ # 必填:TradingView库
├── charting_library.js # 主库脚本
├── charting_library.d.ts
└── ... (其他库文件)How to Get TradingView Library
获取TradingView库的方法
- Request access from TradingView: https://www.tradingview.com/HTML5-stock-forex-bitcoin-charting-library/
- Download the charting library package
- Copy the folder to your
charting_librarydirectorypublic/tradingview/
- 申请访问权限:访问TradingView官网:https://www.tradingview.com/HTML5-stock-forex-bitcoin-charting-library/
- 下载图表库包
- 复制文件夹到你的
charting_library目录public/tradingview/
TradingView Configuration
TradingView配置
tsx
// In your TradingPage component
<TradingPage
symbol={symbol}
tradingViewConfig={{
scriptSRC: '/tradingview/charting_library/charting_library.js',
library_path: '/tradingview/charting_library/',
customCssUrl: '/tradingview/chart.css',
colorConfig: {
upColor: '#26a69a',
downColor: '#ef5350',
},
}}
/>tsx
// 在你的交易页面组件中
<TradingPage
symbol={symbol}
tradingViewConfig={{
scriptSRC: '/tradingview/charting_library/charting_library.js',
library_path: '/tradingview/charting_library/',
customCssUrl: '/tradingview/chart.css',
colorConfig: {
upColor: '#26a69a',
downColor: '#ef5350',
},
}}
/>Tailwind Configuration
Tailwind配置
tailwind.config.ts
tailwind.config.ts
ts
import type { Config } from 'tailwindcss';
import { OUITailwind } from '@orderly.network/ui';
export default {
content: [
'./index.html',
'./src/**/*.{js,ts,jsx,tsx}',
'./node_modules/@orderly.network/**/*.{js,mjs}',
],
presets: [OUITailwind.preset],
theme: {
extend: {},
},
plugins: [],
} satisfies Config;ts
import type { Config } from 'tailwindcss';
import { OUITailwind } from '@orderly.network/ui';
export default {
content: [
'./index.html',
'./src/**/*.{js,ts,jsx,tsx}',
'./node_modules/@orderly.network/**/*.{js,mjs}',
],
presets: [OUITailwind.preset],
theme: {
extend: {},
},
plugins: [],
} satisfies Config;src/styles/index.css
src/styles/index.css
css
@import '@orderly.network/ui/dist/styles.css';
@tailwind base;
@tailwind components;
@tailwind utilities;css
@import '@orderly.network/ui/dist/styles.css';
@tailwind base;
@tailwind components;
@tailwind utilities;Vite Configuration
Vite配置
Important: The wallet connector packages use Node.js built-ins like. You must add polyfills.Buffer
bash
npm install -D vite-plugin-node-polyfills重要提示:钱包连接器包使用了Node.js内置模块如,你必须添加polyfills。Buffer
bash
npm install -D vite-plugin-node-polyfillsvite.config.ts
vite.config.ts
ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { nodePolyfills } from 'vite-plugin-node-polyfills';
import path from 'path';
export default defineConfig({
plugins: [
react(),
nodePolyfills({
include: ['buffer', 'crypto', 'stream', 'util'],
globals: {
Buffer: true,
global: true,
process: true,
},
}),
],
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
});ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { nodePolyfills } from 'vite-plugin-node-polyfills';
import path from 'path';
export default defineConfig({
plugins: [
react(),
nodePolyfills({
include: ['buffer', 'crypto', 'stream', 'util'],
globals: {
Buffer: true,
global: true,
process: true,
},
}),
],
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
});Checklist for Production
生产环境检查清单
- Broker ID configured
- WalletConnect project ID (for mobile wallets)
- Runtime config.js deployed
- TradingView library (if using charts)
- Custom branding (logo, favicon, colors)
- Error tracking (Sentry, etc.)
- Analytics integration
- SSL/HTTPS enabled
- 已配置经纪商ID
- WalletConnect项目ID(用于移动端钱包)
- 已部署运行时config.js
- TradingView库(如果使用图表功能)
- 自定义品牌(logo、favicon、配色)
- 错误追踪(如Sentry)
- 分析集成
- 已启用SSL/HTTPS
Related Skills
相关技能
- orderly-sdk-install-dependency - SDK package installation
- orderly-sdk-wallet-connection - Wallet integration details
- orderly-sdk-page-components - Using pre-built page components
- orderly-sdk-trading-workflows - End-to-end trading implementation
- orderly-sdk-theming - Customizing the UI appearance
- orderly-sdk-debugging - Troubleshooting common issues
- orderly-sdk-install-dependency - SDK包安装
- orderly-sdk-wallet-connection - 钱包集成详情
- orderly-sdk-page-components - 使用预构建页面组件
- orderly-sdk-trading-workflows - 端到端交易实现
- orderly-sdk-theming - 自定义UI外观
- orderly-sdk-debugging - 常见问题排查