@edgex-fe/typescript-sdk
v0.1.4
Published
Official TypeScript SDK for EdgeX API - Comprehensive trading and market data integration
Maintainers
Readme
EdgeX TypeScript SDK API 接口文档
用于 EdgeX 交易平台的 TypeScript SDK,提供完整的交易、认证、WebSocket 和提现功能。
安装
npm install @edgex/typescript-sdk快速开始
import { EdgeXSDK } from '@edgex/typescript-sdk';
// 初始化 SDK
const sdk = new EdgeXSDK({
clientId: 'your-client-id'
});
// 或使用单例模式
const sdk = EdgeXSDK.getInstance({
clientId: 'your-client-id'
});目录
获取 Metadata 配置
Metadata 包含交易所的全局配置信息,包括币种列表、合约信息、链配置等。
API 调用
import { ApiService } from './api/services';
// 获取 metadata 配置
const response = await ApiService.getMetadata();
const metadata = response.data;
// 设置到 SDK 中
sdk.setMetadata(metadata);使用 React Hook
import { useMetadata } from './hooks/useMetadata';
function App() {
const { meta, config, loading } = useMetadata();
useEffect(() => {
if (meta) {
sdk.setMetadata(meta);
}
}, [meta]);
// config 包含:
// - appName: 应用名称 (默认: "edgeX")
// - appEnv: 环境 (默认: "testnet")
// - onlySignOn: 签名域名 (默认: "https://testnet.edgex.exchange")
}Metadata 数据结构
interface Metadata {
global: {
appName: string;
appEnv: string;
appOnlySignOn: string;
starkExChainId: string;
starkExContractAddress: string;
starkExCollateralCoin: {
coinId: string;
coinName: string;
starkExAssetId: string;
starkExResolution: string;
};
// ... 其他全局配置
};
coinList: Array<{
coinId: string;
coinName: string;
stepSize: string;
showStepSize: string;
iconUrl: string;
starkExAssetId: string | null;
starkExResolution: string | null;
}>;
contractList: Array<{
contractId: string;
contractName: string;
baseCoinId: string;
quoteCoinId: string;
tickSize: string;
stepSize: string;
minOrderSize: string;
maxOrderSize: string;
// ... 其他合约配置
}>;
multiChain: {
coinId: string;
maxWithdraw: string;
minWithdraw: string;
chainList: Array<{
chain: string;
chainId: string;
contractAddress: string;
rpcUrl: string;
tokenList: Array<{
tokenAddress: string;
decimals: string;
token: string;
// ... 其他代币配置
}>;
// ... 其他链配置
}>;
};
}登录认证
EdgeX 使用钱包签名进行身份认证,需要生成 API 密钥和 L2 密钥对。
1. 生成 API 密钥
API 密钥用于调用私有 API 接口。
import { createWalletClient, custom, getAddress } from "viem";
import { usePrivy, useWallets } from "@privy-io/react-auth";
// 生成 API 密钥
async function generateApiKey() {
const { user } = usePrivy();
const { wallets } = useWallets();
const effectiveAddress = user?.wallet?.address;
if (!effectiveAddress) throw new Error("钱包未连接");
// 获取 Privy 钱包
const privyWallet = wallets.find(
w => w.address.toLowerCase() === effectiveAddress.toLowerCase()
);
// 创建钱包客户端
const walletClient = createWalletClient({
account: getAddress(effectiveAddress),
chain: currentChain,
transport: custom({
async request({ method, params }) {
const provider = await privyWallet.getEthereumProvider();
return await provider.request({ method, params });
},
}),
});
// 签名消息
const message = `action: edgeX Onboard\nonlySignOn: https://testnet.edgex.exchange`;
const signature = await walletClient.signMessage({
account: getAddress(effectiveAddress),
message,
});
// 生成 API 密钥
const apiKeys = sdk.generateApiKeyFromSignature(signature);
sdk.setApiKeys(apiKeys);
return apiKeys;
}2. 生成 L2 密钥对
L2 密钥对用于 StarkEx 层的交易签名。
// 生成 L2 密钥对
async function generateL2KeyPair() {
const clientAccountId = "main";
const message = `name: edgeX\nenvId: testnet\naction: L2 Key\nonlySignOn: https://testnet.edgex.exchange\nclientAccountId: ${clientAccountId}`;
const signature = await walletClient.signMessage({
account: getAddress(effectiveAddress),
message,
});
// 生成 L2 密钥对
const l2KeyPair = sdk.generateL2KeyPairFromSignature(signature);
sdk.setL2KeyPair(l2KeyPair);
return l2KeyPair;
}3. 生成认证头
使用 API 密钥生成请求认证头。
// 生成认证头
const headers = sdk.createAuthHeaders({
method: 'GET',
path: '/api/v1/private/user/getUserInfo',
timestamp: Date.now().toString(),
params: { userId: '123456' }, // 可选的查询参数
});
// headers 包含:
// {
// 'X-EdgeX-Api-Key': 'your-api-key',
// 'X-EdgeX-Passphrase': 'your-passphrase',
// 'X-EdgeX-Signature': 'generated-signature',
// 'X-EdgeX-Timestamp': 'timestamp'
// }4. 密钥数据结构
// API 密钥结构
interface ApiKeys {
apiKey: string;
apiPassphrase: string;
apiSecret: string;
}
// L2 密钥对结构
interface L2KeyPair {
l2PrivateKey: string;
l2PublicKey: string;
l2PublicKeyY: string;
}创建订单
创建交易订单需要使用 L2 签名进行 StarkEx 层验证。
1. 订单参数
import { TradeOrderParams } from '@edgex/typescript-sdk';
const tradeParams: TradeOrderParams = {
price: '30.000', // 订单价格
size: '1.0', // 订单数量
type: 'LIMIT', // 订单类型: LIMIT | MARKET
timeInForce: 'GOOD_TIL_CANCEL', // 时效性: GOOD_TIL_CANCEL | IMMEDIATE_OR_CANCEL | FILL_OR_KILL
reduceOnly: false, // 是否只减仓
isPositionTpsl: false, // 是否为止盈止损单
isSetOpenTp: false, // 是否设置开仓止盈
isSetOpenSl: false, // 是否设置开仓止损
contractId: '20000018', // 合约ID (AVAXUSD)
side: 'BUY', // 买卖方向: BUY | SELL
triggerPrice: '', // 触发价格 (条件单)
triggerPriceType: 'LAST_PRICE', // 触发价格类型
extraType: '', // 额外类型
extraDataJson: '', // 额外数据
accountId: 'your-account-id', // 账户ID
};2. 生成订单签名
// 获取合约信息
const symbolInfo = {
contractId: '20000018',
symbol: 'AVAXUSD',
contractName: 'AVAXUSD',
oraclePrice: '29.641',
tickSize: '0.001',
takerFeeRate: '0.001',
makerFeeRate: '0.001',
};
// 链信息
const chainInfo = {
chainId: '11155111', // Sepolia testnet
};
// 账户信息
const accountInfo = {
accountId: 'your-account-id',
};
// 生成交易参数和签名
const result = await sdk.generateTradeParams({
tradeParams,
symbolInfo,
chainInfo,
accountInfo,
requestPath: '/api/v1/private/order/createOrder',
requestMethod: 'POST',
});
// result 包含:
// {
// headers: { /* 认证头 */ },
// params: {
// ...tradeParams,
// l2Signature: 'generated-l2-signature'
// }
// }3. 发送订单请求
import { apiClient } from './api/client';
// 创建订单
async function createOrder(tradeParams: TradeOrderParams) {
// 生成签名参数
const { headers, params } = await sdk.generateTradeParams({
tradeParams,
symbolInfo,
chainInfo,
accountInfo,
requestPath: '/api/v1/private/order/createOrder',
requestMethod: 'POST',
});
// 发送请求
const response = await apiClient.post('/api/v1/private/order/createOrder', params, {
headers,
});
return response.data;
}4. 订单类型说明
| 类型 | 说明 |
|------|------|
| LIMIT | 限价单 - 指定价格执行 |
| MARKET | 市价单 - 按市场价格立即执行 |
5. 时效性说明
| 类型 | 说明 |
|------|------|
| GOOD_TIL_CANCEL | 有效直到取消 |
| IMMEDIATE_OR_CANCEL | 立即执行或取消 |
| FILL_OR_KILL | 全部成交或取消 |
获取 WebSocket 连接
EdgeX 提供公有和私有两种 WebSocket 连接,分别用于获取市场数据和用户数据。
1. 公有 WebSocket (市场数据)
用于获取行情、K线、深度等公开市场数据,无需认证。
import { usePublicWebSocket } from './hooks/usePublicWebSocket';
function MarketData() {
const {
wsStatus,
connect,
disconnect,
subscribe,
unsubscribe,
isConnected
} = usePublicWebSocket({
wsHost: 'wss://quote-testnet.edgex.exchange',
onMessage: (data) => {
console.log('收到市场数据:', data);
// 处理行情数据
if (data.type === 'ticker') {
// 处理ticker数据
}
}
});
// 连接WebSocket
const handleConnect = () => {
connect();
};
// 订阅行情数据
useEffect(() => {
if (isConnected) {
subscribe('ticker.all.1s'); // 订阅所有ticker 1秒数据
subscribe('depth.BTCUSD.20'); // 订阅BTC深度数据
subscribe('kline.ETHUSD.1m'); // 订阅ETH 1分钟K线
}
}, [isConnected]);
return (
<div>
<p>状态: {wsStatus}</p>
<button onClick={handleConnect} disabled={isConnected}>
连接市场数据
</button>
</div>
);
}2. 私有 WebSocket (用户数据)
用于获取持仓、订单、账户余额等私有数据,需要 API 签名认证。
import { usePrivateWebSocket } from './hooks/usePrivateWebSocket';
function UserData({ apiOutput, l2Output }) {
const {
wsStatus,
connect,
disconnect,
isConnected
} = usePrivateWebSocket({
apiOutput, // API密钥
l2Output, // L2密钥对
wsHost: 'wss://quote-testnet.edgex.exchange',
onMessage: (data) => {
console.log('收到用户数据:', data);
// 处理用户数据更新
if (data.type === 'trade-event' || data.type === 'assets-event') {
const { collateral, position, order } = data.content?.data || {};
// 更新资产信息
if (collateral) {
updateCollateralInfo(collateral);
}
// 更新持仓信息
if (position) {
updatePositionInfo(position);
}
// 更新订单信息
if (order) {
updateOrderInfo(order);
}
}
}
});
return (
<div>
<p>状态: {wsStatus}</p>
<button
onClick={connect}
disabled={isConnected || !apiOutput || !l2Output}
>
连接用户数据
</button>
</div>
);
}3. WebSocket 消息类型
公有消息类型
ticker- 行情数据depth- 深度数据kline- K线数据trade- 成交数据
私有消息类型
trade-event- 交易事件 (订单成交、持仓变化)assets-event- 资产事件 (余额变化)order-event- 订单事件 (订单状态变化)
4. 订阅频道格式
// 行情订阅
subscribe('ticker.all.1s'); // 所有合约1秒ticker
subscribe('ticker.BTCUSD.1s'); // BTC合约1秒ticker
// 深度订阅
subscribe('depth.BTCUSD.20'); // BTC 20档深度
subscribe('depth.ETHUSD.50'); // ETH 50档深度
// K线订阅
subscribe('kline.BTCUSD.1m'); // BTC 1分钟K线
subscribe('kline.ETHUSD.5m'); // ETH 5分钟K线ETH 链提现
EdgeX 的普通(慢速)提现会把资金直接打回以太坊主链或测试网,接口为 /api/v1/private/assets/createNormalWithdraw。EdgeXSDK.generateETHWithdrawParams 默认就是针对这个接口构造 L2 签名和认证头,流程如下:
1. 构造 ETHWithdrawInput
import { ETHWithdrawInput, generateRandomClientId } from '@edgex/typescript-sdk';
const withdrawInput: ETHWithdrawInput = {
accountId: currentAccount.accountId, // 账户 ID
coinId: metadata.multiChain.coinId, // 例如 1000 = USDT
amount: '100.0', // 提现金额(字符串)
ethAddress: currentAccount.ethAddress, // 目标 L1 地址
clientWithdrawId: generateRandomClientId(),
expireTime: (
Math.ceil(Date.now() / 3600000) * 3600000 + 14 * 24 * 60 * 60 * 1000
).toString(), // 向上取整至整点 + 14 天
};
clientWithdrawId必须全局唯一,可使用 SDK 自带的generateRandomClientId()或自定义 UUID。
2. 生成 L2 签名与认证头
async function createETHWithdraw(withdrawInput: ETHWithdrawInput, networkId: number) {
// networkId: 1 = Mainnet, 5 = Goerli, 11155111 = Sepolia
return sdk.generateETHWithdrawParams(withdrawInput, networkId);
// 返回 { headers, params: { ...withdrawInput, l2Signature } }
}如需自定义路由,可使用 sdk.generateETHWithdrawParams(input, networkId, '/api/v1/private/assets/createNormalWithdraw') 显式指定 requestPath。
3. 调用 createNormalWithdraw
import { apiClient } from './api/client';
async function executeETHWithdraw(input: ETHWithdrawInput, networkId: number) {
const { headers, params } = await createETHWithdraw(input, networkId);
const response = await apiClient.post(
'/api/v1/private/assets/createNormalWithdraw',
params,
{ headers },
);
return response.data; // data.withdrawId 可用于状态查询
}4. 支持的网络
| Network ID | 网络名称 | 说明 |
|------------|----------|------|
| 1 | Ethereum Mainnet | 以太坊主网 |
| 5 | Goerli Testnet | Goerli 测试网 |
| 11155111 | Sepolia Testnet | Sepolia 测试网 |
5. 提现状态查询
createNormalWithdraw 返回的 withdrawId 可通过 /api/v1/private/assets/getNormalWithdrawById 查询状态:
async function getNormalWithdrawStatus(accountId: string, withdrawId: string) {
const params = { accountId, normalWithdrawIdList: withdrawId };
const headers = sdk.createAuthHeaders({
method: 'GET',
path: '/api/v1/private/assets/getNormalWithdrawById',
params,
});
const response = await apiClient.get(
'/api/v1/private/assets/getNormalWithdrawById',
{ headers, params },
);
return response.data;
}快速提现
快速提现依赖 LP 在 L1 垫付资金,需要提前查询 LP 公钥、Fact 合约地址以及费用。前端必须按照以下步骤调用 /api/v1/private/assets/createFastWithdraw:
1. 查询快速提现签名信息
import { apiClient } from './api/client';
async function getFastWithdrawSignInfo(params: {
chainId: number;
amount: string;
tokenAddress?: string;
}) {
const headers = sdk.createAuthHeaders({
method: 'GET',
path: '/api/v1/private/assets/getFastWithdrawSignInfo',
params,
});
const response = await apiClient.get(
'/api/v1/private/assets/getFastWithdrawSignInfo',
{ headers, params },
);
return response.data.data; // fee / lpAccountId / fastWithdrawL2Key / fastWithdrawFactRegisterAddress / fastWithdrawMaxAmount
}chainId:目标 L1 链 ID。amount:用户希望提现的数量(未扣手续费)。tokenAddress:目标代币的 L1 合约地址,原生资产可留空。
若 amount ≥ fastWithdrawMaxAmount 必须提示用户重新输入。
2. 构造并签名条件转账
import BigNumber from 'bignumber.js';
import { SignableConditionalTransfer } from '@edgex/typescript-sdk/starkex-lib';
import { generateRandomClientId } from '@edgex/typescript-sdk';
import { encodePacked, keccak256, parseUnits, sha256, stringToHex } from 'viem';
function buildFastWithdrawFact(opts: {
recipient: string;
tokenAddress: string;
decimals: number;
humanAmount: string;
salt: bigint;
}) {
return keccak256(
encodePacked(
['address', 'uint256', 'address', 'uint256'],
[
opts.recipient,
parseUnits(opts.humanAmount, opts.decimals),
opts.tokenAddress,
opts.salt,
],
),
);
}
async function createFastWithdrawPayload({
accountId,
coinId,
ethAddress,
token,
amount,
chainId,
}: {
accountId: string;
coinId: string;
ethAddress: string;
token: { tokenAddress: string; decimals: number };
amount: string;
chainId: number;
}) {
const info = await getFastWithdrawSignInfo({
chainId,
amount,
tokenAddress: token.tokenAddress,
});
if (BigNumber(amount).gte(info.fastWithdrawMaxAmount)) {
throw new Error('超过快速提现限额');
}
const clientFastWithdrawId = generateRandomClientId();
const expireTime =
Math.ceil(Date.now() / 3600000) * 3600000 + 14 * 24 * 60 * 60 * 1000;
const salt = BigInt(sha256(stringToHex(clientFastWithdrawId)));
const fact = buildFastWithdrawFact({
recipient: ethAddress,
tokenAddress: token.tokenAddress,
decimals: token.decimals,
humanAmount: amount,
salt,
});
const debitAmount = BigNumber(amount).plus(info.fee).toString();
const transferToSign = {
senderPositionId: accountId,
receiverPositionId: info.lpAccountId,
receiverPublicKey: info.fastWithdrawL2Key,
factRegistryAddress: info.fastWithdrawFactRegisterAddress,
fact,
humanAmount: debitAmount,
clientId: clientFastWithdrawId,
expirationIsoTimestamp: `${expireTime}`,
};
const l2KeyPair = sdk.getL2KeyPair();
if (!l2KeyPair) throw new Error('请先设置 L2 密钥对');
const signable = SignableConditionalTransfer.fromTransfer(transferToSign, chainId);
const l2Signature = await signable.sign(l2KeyPair.l2PrivateKey);
const requestBody = {
accountId,
coinId,
amount,
ethAddress,
erc20Address: token.tokenAddress,
lpAccountId: info.lpAccountId,
clientFastWithdrawId,
expireTime: `${expireTime}`,
l2Signature,
fee: info.fee,
chainId,
factRegistryAddress: info.fastWithdrawFactRegisterAddress,
fact,
};
const headers = sdk.createAuthHeaders({
method: 'POST',
path: '/api/v1/private/assets/createFastWithdraw',
body: requestBody,
});
return { headers, params: requestBody };
}关键字段说明:
amount:用户期望收到的数量;LP 会额外扣除fee,因此签名时需要debitAmount = amount + fee。fact:对(recipient, amountInWei, tokenAddress, salt)执行 Soliditykeccak256的结果,必须与 LP Fact 合约匹配。lpAccountId/fastWithdrawL2Key/factRegistryAddress:全部来自第一步的接口响应。
3. 提交快速提现
async function executeFastWithdraw(options: {
accountId: string;
coinId: string;
ethAddress: string;
token: { tokenAddress: string; decimals: number };
amount: string;
chainId: number;
}) {
const { headers, params } = await createFastWithdrawPayload(options);
const response = await apiClient.post(
'/api/v1/private/assets/createFastWithdraw',
params,
{ headers },
);
return response.data; // data.fastWithdrawId
}4. 快速提现状态查询
async function getFastWithdrawStatus(accountId: string, fastWithdrawId: string) {
const params = { accountId, fastWithdrawIdList: fastWithdrawId };
const headers = sdk.createAuthHeaders({
method: 'GET',
path: '/api/v1/private/assets/getFastWithdrawById',
params,
});
const response = await apiClient.get(
'/api/v1/private/assets/getFastWithdrawById',
{ headers, params },
);
return response.data;
}跨链提现
跨链提现(速度中等)会通过 LP 把资金从 EdgeX L2 转移到其他 L1,接口 /api/v1/private/assets/createCrossWithdraw 需要多个参数来自 getCrossWithdrawSignInfo,并且提交的 amount 需要先扣除手续费。
1. 查询跨链提现签名信息
async function getCrossWithdrawSignInfo(params: {
chainId: number;
amount: string;
tokenAddress?: string;
}) {
const headers = sdk.createAuthHeaders({
method: 'GET',
path: '/api/v1/private/assets/getCrossWithdrawSignInfo',
params,
});
const response = await apiClient.get(
'/api/v1/private/assets/getCrossWithdrawSignInfo',
{ headers, params },
);
return response.data.data; // fee / lpAccountId / crossWithdrawL2Key / crossWithdrawMaxAmount
}与快速提现类似,amount 不能超过 crossWithdrawMaxAmount。
2. 生成跨链提现参数
import BigNumber from 'bignumber.js';
import { CrossWithdrawInput, generateRandomClientId } from '@edgex/typescript-sdk';
async function buildCrossWithdrawInput({
accountId,
coinId,
chainId,
amount,
tokenAddress,
receiverAddress,
}: {
accountId: string;
coinId: string;
chainId: number;
amount: string;
tokenAddress: string;
receiverAddress: string;
}) {
const info = await getCrossWithdrawSignInfo({
chainId,
amount,
tokenAddress,
});
if (BigNumber(amount).gte(info.crossWithdrawMaxAmount)) {
throw new Error('超过跨链提现限额');
}
const clientCrossWithdrawId = generateRandomClientId();
const expireTime =
Math.ceil(Date.now() / 3600000) * 3600000 + 14 * 24 * 60 * 60 * 1000;
const crossWithdrawInput: CrossWithdrawInput = {
accountId,
coinId,
amount: BigNumber(amount).minus(info.fee).toString(), // 注意:真正提交的 amount 已扣手续费
ethAddress: receiverAddress,
erc20Address: tokenAddress,
lpAccountId: info.lpAccountId,
clientCrossWithdrawId,
expireTime: `${expireTime}`,
fee: info.fee,
chainId,
crossWithdrawL2Key: info.crossWithdrawL2Key,
};
return crossWithdrawInput;
}3. 计算签名并发送 createCrossWithdraw
import { apiClient } from './api/client';
async function executeCrossWithdraw(options: {
accountId: string;
coinId: string;
chainId: number;
amount: string;
tokenAddress: string;
receiverAddress: string;
extraAaPayload?: {
mpcAddress: string;
mpcSignature: string;
mpcSignTime: string;
};
}) {
const input = await buildCrossWithdrawInput(options);
const { headers, params } = await sdk.generateCrossWithdrawParams(
input,
options.chainId,
);
const { crossWithdrawL2Key, ...body } = params; // API 不接受该字段
const response = await apiClient.post(
'/api/v1/private/assets/createCrossWithdraw',
{
...body,
...(options.extraAaPayload ?? {}),
},
{ headers },
);
return response.data; // data.crossWithdrawId
}4. MPC / AA 提现附加字段
当链配置 allowAaWithdraw = true 且当前钱包类型为 MPC,需要额外携带:
mpcAddress:MPC 智能钱包地址。mpcSignTime:秒级 Unix 时间戳。mpcSignature:对receiverAddress + (amount-after-fee 按 decimals 转 uint256) + tokenAddress + mpcSignTime + chainId做 keccak256 后使用 MPC 钱包signMessage的结果。ethAddress:最终实际接收地址(会覆盖上面 body 中的ethAddress)。
计算方式可直接参考 apps/web/src/modules/trade/components/withdraw/useWithdraw.ts 中的实现(normalizeHex32、bnToHex32、signMessage 等 helper)。
5. 跨链提现状态查询
async function getCrossWithdrawStatus(accountId: string, crossWithdrawId: string) {
const params = { accountId, crossWithdrawIdList: crossWithdrawId };
const headers = sdk.createAuthHeaders({
method: 'GET',
path: '/api/v1/private/assets/getCrossWithdrawById',
params,
});
const response = await apiClient.get(
'/api/v1/private/assets/getCrossWithdrawById',
{ headers, params },
);
return response.data;
}6. 获取链配置信息
function getChainConfig(metadata: any, chainId: string) {
const multiChain = metadata.multiChain;
return multiChain.chainList.find((chain: any) => chain.chainId === chainId);
}
const chainConfig = getChainConfig(metadata, '97'); // BSC testnet
console.log('链配置:', {
chain: chainConfig.chain,
contractAddress: chainConfig.contractAddress,
rpcUrl: chainConfig.rpcUrl,
tokenList: chainConfig.tokenList,
});示例链配置:
| Chain ID | 网络名称 | 代币示例 |
|----------|----------|----------|
| 97 | BSC Testnet | USDT, BUSD |
| 56 | BSC Mainnet | USDT, BUSD |
| 421614 | Arbitrum Sepolia | USDT, USDC |
| 42161 | Arbitrum One | USDT, USDC |
完整示例
以下是一个完整的 React 应用示例,展示了如何使用 EdgeX TypeScript SDK 的所有主要功能。
1. 主应用组件
import React, { useCallback, useEffect, useMemo, useState } from "react";
import { usePrivy, useWallets } from "@privy-io/react-auth";
import { useAccount, useChainId } from "wagmi";
import { createWalletClient, custom, getAddress } from "viem";
import { EdgeXSDK } from "@edgex/typescript-sdk";
import { useMetadata } from "./hooks/useMetadata";
import { usePublicWebSocket } from "./hooks/usePublicWebSocket";
import { usePrivateWebSocket } from "./hooks/usePrivateWebSocket";
export function App() {
const { authenticated, login, logout, user } = usePrivy();
const { wallets } = useWallets();
const { address } = useAccount();
const chainId = useChainId();
const [apiOutput, setApiOutput] = useState<any | null>(null);
const [l2Output, setL2Output] = useState<any | null>(null);
// 获取metadata配置
const { config: metaConfig, meta } = useMetadata();
const edgeXSDK = useMemo(() => {
return EdgeXSDK.getInstance({
clientId: "client_001",
});
}, []);
useEffect(() => {
if (meta) {
edgeXSDK.setMetadata(meta);
}
}, [meta]);
// 公有WebSocket连接 (市场数据)
const {
wsStatus: publicWsStatus,
connect: publicWsConnect,
subscribe: publicWsSubscribe,
} = usePublicWebSocket({
onMessage: (data) => {
console.log("收到市场数据:", data);
},
});
// 私有WebSocket连接 (用户数据)
const {
wsStatus: privateWsStatus,
connect: privateWsConnect,
} = usePrivateWebSocket({
apiOutput,
l2Output,
onMessage: (data) => {
console.log("收到用户数据:", data);
},
});
// 生成API密钥
const generateApiKey = useCallback(async () => {
try {
const effectiveAddress = address ?? (user?.wallet?.address as string);
if (!effectiveAddress) throw new Error("钱包未连接");
const privyWallet = wallets.find(
w => w.address.toLowerCase() === effectiveAddress.toLowerCase()
);
if (!privyWallet) throw new Error("未找到钱包");
const walletClient = createWalletClient({
account: getAddress(effectiveAddress),
chain: currentChain,
transport: custom({
async request({ method, params }) {
const provider = await privyWallet.getEthereumProvider();
return await provider.request({ method, params });
},
}),
});
const message = `action: ${metaConfig.appName} Onboard\nonlySignOn: ${metaConfig.onlySignOn}`;
const signature = await walletClient.signMessage({
account: getAddress(effectiveAddress),
message,
});
const result = edgeXSDK.generateApiKeyFromSignature(signature);
setApiOutput(result);
edgeXSDK.setApiKeys(result);
} catch (error) {
console.error("生成API密钥失败:", error);
}
}, [address, user, wallets, metaConfig]);
// 生成L2密钥对
const generateL2KeyPair = useCallback(async () => {
try {
const effectiveAddress = address ?? (user?.wallet?.address as string);
if (!effectiveAddress) throw new Error("钱包未连接");
const privyWallet = wallets.find(
w => w.address.toLowerCase() === effectiveAddress.toLowerCase()
);
if (!privyWallet) throw new Error("未找到钱包");
const walletClient = createWalletClient({
account: getAddress(effectiveAddress),
chain: currentChain,
transport: custom({
async request({ method, params }) {
const provider = await privyWallet.getEthereumProvider();
return await provider.request({ method, params });
},
}),
});
const clientAccountId = "main";
const message = `name: ${metaConfig.appName}\nenvId: ${metaConfig.appEnv}\naction: L2 Key\nonlySignOn: ${metaConfig.onlySignOn}\nclientAccountId: ${clientAccountId}`;
const signature = await walletClient.signMessage({
account: getAddress(effectiveAddress),
message,
});
const result = edgeXSDK.generateL2KeyPairFromSignature(signature);
setL2Output(result);
edgeXSDK.setL2KeyPair(result);
} catch (error) {
console.error("生成L2密钥失败:", error);
}
}, [address, user, wallets, metaConfig]);
// 自动订阅市场数据
useEffect(() => {
if (publicWsStatus === "CONNECTED") {
publicWsSubscribe("ticker.all.1s");
}
}, [publicWsStatus, publicWsSubscribe]);
return (
<div style={{ padding: 24, fontFamily: "ui-sans-serif, system-ui" }}>
<h1>EdgeX SDK 完整示例</h1>
{!authenticated ? (
<button onClick={login}>连接钱包</button>
) : (
<div>
<p>地址: {address || user?.wallet?.address}</p>
<div style={{ display: "flex", gap: 8, marginBottom: 16 }}>
<button onClick={generateApiKey}>生成API密钥</button>
<button onClick={generateL2KeyPair}>生成L2密钥</button>
<button onClick={logout}>登出</button>
</div>
<div style={{ display: "flex", gap: 8, marginBottom: 16 }}>
<button onClick={publicWsConnect}>连接市场数据</button>
<button onClick={privateWsConnect}>连接用户数据</button>
</div>
{apiOutput && (
<div>
<h3>API密钥</h3>
<pre>{JSON.stringify(apiOutput, null, 2)}</pre>
</div>
)}
{l2Output && (
<div>
<h3>L2密钥对</h3>
<pre>{JSON.stringify(l2Output, null, 2)}</pre>
</div>
)}
</div>
)}
</div>
);
}2. 交易示例
import { TradeOrderParams } from '@edgex/typescript-sdk';
import { apiClient } from './api/client';
// 创建限价买单
async function createBuyOrder() {
const tradeParams: TradeOrderParams = {
price: '30000.0',
size: '0.001',
type: 'LIMIT',
timeInForce: 'GOOD_TIL_CANCEL',
reduceOnly: false,
isPositionTpsl: false,
isSetOpenTp: false,
isSetOpenSl: false,
contractId: '10000001', // BTCUSD
side: 'BUY',
triggerPrice: '',
triggerPriceType: 'LAST_PRICE',
extraType: '',
extraDataJson: '',
accountId: 'your-account-id',
};
const { headers, params } = await sdk.generateTradeParams({
tradeParams,
symbolInfo: {
contractId: '10000001',
symbol: 'BTCUSD',
contractName: 'BTCUSD',
oraclePrice: '30000.0',
tickSize: '0.1',
takerFeeRate: '0.00038',
makerFeeRate: '0.00015',
},
chainInfo: { chainId: '11155111' },
accountInfo: { accountId: 'your-account-id' },
requestPath: '/api/v1/private/order/createOrder',
requestMethod: 'POST',
});
const response = await apiClient.post('/api/v1/private/order/createOrder', params, {
headers,
});
return response.data;
}3. 提现示例
// ETH链提现
async function withdrawToETH() {
const withdrawInput = {
accountId: 'your-account-id',
coinId: '1000',
amount: '100.0',
ethAddress: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6',
clientWithdrawId: Date.now().toString(),
expireTime: (Date.now() + 24 * 60 * 60 * 1000).toString(),
};
const { headers, params } = await sdk.generateETHWithdrawParams(withdrawInput, 11155111);
const response = await apiClient.post('/api/v1/private/withdraw/eth', params, {
headers,
});
return response.data;
}
// 跨链提现到BSC
async function withdrawToBSC() {
const crossWithdrawInput = {
accountId: 'your-account-id',
coinId: '1000',
amount: '50.0',
ethAddress: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6',
erc20Address: '0xda6c748A7593826e410183F05893dbB363D025a1',
lpAccountId: '642774866310726088',
clientCrossWithdrawId: Date.now().toString(),
expireTime: (Date.now() + 24 * 60 * 60 * 1000).toString(),
fee: '1.0',
chainId: 97,
crossWithdrawL2Key: '0x06038e1248a1caea0e9c3d7f61e0a00fb928f6775a74d8ac35845cfa44eecb55',
};
const { headers, params } = await sdk.generateCrossWithdrawParams(crossWithdrawInput, 97);
const response = await apiClient.post('/api/v1/private/withdraw/cross', params, {
headers,
});
return response.data;
}错误处理
常见错误类型
- 认证错误 - API密钥无效或过期
- 签名错误 - L2签名验证失败
- 参数错误 - 请求参数格式不正确
- 网络错误 - WebSocket连接失败
- 余额不足 - 账户余额不足以完成操作
错误处理示例
try {
const result = await createBuyOrder();
console.log('订单创建成功:', result);
} catch (error) {
if (error.response?.status === 401) {
console.error('认证失败,请重新生成API密钥');
} else if (error.response?.status === 400) {
console.error('参数错误:', error.response.data.message);
} else {
console.error('未知错误:', error.message);
}
}