@shenzheyu/extended
v0.0.1-alpha.0
Published
TypeScript SDK for the Extended Starknet API.
Downloads
5
Readme
Extended TypeScript SDK
用于调用 Extended API 的 TypeScript SDK,覆盖公开 REST、私有 REST、WebSocket 订阅与账户 Onboarding 流程。
环境要求
- Node.js >= 18(仅支持 Node 运行时,ESM 包)
依赖说明
运行时依赖均在 package.json 中固定版本,核心依赖包括:
@x10xchange/stark-crypto-wrapper-wasm:Stark 签名/哈希(WASM 优先)starknet:JS 回退实现ethers:L1 签名(Onboarding)zodv4:Schema 校验ws:WebSocket 传输
请求头与错误处理
- SDK 默认会设置
User-Agent(<包名>/<版本>),可通过userAgent覆盖。 - 当 REST 返回 HTTP 200 但响应
status=ERROR时,会抛出ApiError,并附带code与原始响应数据。
兼容性说明
getPositions在部分环境会省略maxPositionSize、createdTime、updatedTime,SDK 会将其解析为undefined。
安装
如果已发布到 npm:
npm install extended-sdk从源码使用:
npm install
npm run buildnpm 包仅包含 dist 构建产物,源码请查看仓库。
开发与测试
单元测试覆盖 onboarding 客户端关键流程。
npm run test集成测试(Onboarding)
集成测试会真实调用 Extended API,默认跳过。请复制 .env.example 为 .env 并填写:
EXTENDED_INTEGRATION_ENABLED=trueEXTENDED_INTEGRATION_L1_PRIVATE_KEY=0x...- 可选:
EXTENDED_INTEGRATION_NETWORK=testnet(或mainnet) - 可选:
EXTENDED_INTEGRATION_ONBOARDING_BASE_URL=https://... - 可选:
EXTENDED_INTEGRATION_SIGNING_DOMAIN=... - 可选(有副作用):
EXTENDED_INTEGRATION_ENABLE_API_KEY=true - 可选(有副作用):
EXTENDED_INTEGRATION_SUBACCOUNT_INDEX=2
配置完成后执行 npm run test 即可运行集成测试。
启用副作用选项会在服务端创建新的 API key 或子账户,请确认测试环境可接受。
集成测试(PrivateClient)
私有接口集成测试会真实调用 getPositions 并打印结果,默认跳过。请补充以下变量:
EXTENDED_INTEGRATION_API_KEY=...EXTENDED_INTEGRATION_L2_PRIVATE_KEY=0x...- 可选:
EXTENDED_INTEGRATION_API_BASE_URL=https://...
同样通过 npm run test 触发;未启用时会跳过该测试。
快速开始(PublicClient)
import { HttpTransport, PublicClient, STARKNET_MAINNET } from "extended-sdk";
const transport = new HttpTransport({
baseUrl: STARKNET_MAINNET.apiBaseUrl,
timeoutMs: 10_000,
});
const client = new PublicClient({
endpoint: STARKNET_MAINNET,
transport,
});
const markets = await client.info.getMarkets();
const orderbook = await client.info.getOrderbook("ETH-USD");公开历史行情
const trades = await client.info.getMarketTrades("ETH-USD");
const candles = await client.info.getCandlesHistory("ETH-USD", "trades", {
interval: "PT1M",
limit: 100,
});
const funding = await client.info.getFundingRatesHistory("ETH-USD", {
startTime: Date.now() - 7 * 24 * 60 * 60 * 1000,
endTime: Date.now(),
limit: 200,
});
const openInterests = await client.info.getOpenInterestHistory("ETH-USD", {
interval: "P1H",
startTime: Date.now() - 7 * 24 * 60 * 60 * 1000,
endTime: Date.now(),
});
if (funding.pagination?.cursor) {
await client.info.getFundingRatesHistory("ETH-USD", {
startTime: Date.now() - 7 * 24 * 60 * 60 * 1000,
endTime: Date.now(),
cursor: funding.pagination.cursor,
limit: 200,
});
}私有接口(PrivateClient)
私有接口需要 apiKey 与 signer。signer 用于生成订单相关的签名与密钥信息。
import {
HttpTransport,
PrivateClient,
STARKNET_MAINNET,
StarknetSigner,
} from "extended-sdk";
const transport = new HttpTransport({
baseUrl: STARKNET_MAINNET.apiBaseUrl,
});
const signer = new StarknetSigner({
privateKey: process.env.EXTENDED_L2_PRIVATE_KEY ?? "",
});
const client = new PrivateClient({
endpoint: STARKNET_MAINNET,
transport,
apiKey: process.env.EXTENDED_API_KEY,
signer,
});
const account = await client.account.getAccount();
const fees = await client.account.getFees({ market: "ETH-USD" });下单时请按照 OrderRequest 构造参数并提供结算签名,字段含义请参考 Extended-API.md。
常用示例
Onboarding(账户注册)
L1 签名使用 ethers v6:
import { Wallet } from "ethers";
import { HttpTransport, OnboardingClient, STARKNET_TESTNET } from "extended-sdk";
const transport = new HttpTransport({
baseUrl: STARKNET_TESTNET.onboardingBaseUrl,
});
const wallet = new Wallet(process.env.EXTENDED_L1_PRIVATE_KEY ?? "");
const onboarding = new OnboardingClient({
endpoint: STARKNET_TESTNET,
transport,
l1Signer: {
getAddress: () => wallet.getAddress(),
signMessage: (message) => wallet.signMessage(message),
signTypedData: (domain, types, value) => wallet.signTypedData(domain, types, value),
},
});
const onboarded = await onboarding.onboard();
const accountId = onboarded.account.accountId?.toString();
if (!accountId) {
throw new Error("accountId missing");
}
const apiKey = await onboarding.createAccountApiKey(accountId, "trading api key");私有接口初始化(下单/转账/提现通用)
import {
HttpTransport,
PrivateClient,
STARKNET_TESTNET,
StarknetSigner,
} from "extended-sdk";
const transport = new HttpTransport({
baseUrl: STARKNET_TESTNET.apiBaseUrl,
});
const signer = new StarknetSigner({
privateKey: process.env.EXTENDED_L2_PRIVATE_KEY ?? "",
});
const client = new PrivateClient({
endpoint: STARKNET_TESTNET,
transport,
apiKey: process.env.EXTENDED_API_KEY,
signer,
});账户数据查询
const account = await client.account.getAccount();
const balance = await client.account.getBalance();
const positions = await client.account.getPositions({ market: ["ETH-USD"], side: "LONG" });
const { data: trades, pagination } = await client.account.getTrades({
market: ["ETH-USD"],
limit: 50,
});
if (pagination?.cursor) {
const nextPage = await client.account.getTrades({
market: ["ETH-USD"],
cursor: pagination.cursor,
limit: 50,
});
console.log(nextPage.data.length);
}撤单/批量撤单/Dead Man's Switch
// 单笔撤单(按订单 ID)
await client.orders.cancelOrder("1784963886257016832");
// 单笔撤单(按 externalId)
await client.orders.cancelOrderByExternalId("ext-1");
// 批量撤单(可按 id / 市场 / 全部)
await client.orders.massCancel({
orderIds: ["1784963886257016832", "1784963886257016833"],
});
// 设置 dead man's switch(秒),0 表示取消定时
await client.orders.setDeadmanswitch(60);下单
import { createOrderRequest } from "extended-sdk";
const [market] = await client.info.getMarkets({ market: ["BTC-USD"] });
if (!market) {
throw new Error("market not found");
}
const [fees] = await client.account.getFees({ market: "BTC-USD" });
if (!fees) {
throw new Error("fees not found");
}
const account = await client.account.getAccount();
const orderRequest = await createOrderRequest({
market,
fees,
signer,
vaultId: account.l2Vault,
side: "SELL",
qty: "0.001",
price: "43445.1168",
type: "LIMIT",
timeInForce: "GTT",
selfTradeProtectionLevel: "ACCOUNT",
starknetDomain: STARKNET_TESTNET.starknetDomain,
});
const placed = await client.orders.placeOrder(orderRequest);转账(子账户间)
import { createTransferRequest } from "extended-sdk";
// onboarding 实例见上方 Onboarding 示例
const onboardingAccounts = await onboarding.getAccounts();
const fromAccount = onboardingAccounts[0]?.account;
const toAccount = onboardingAccounts[1]?.account;
if (!fromAccount || !toAccount) {
throw new Error("accounts not found");
}
const transferRequest = await createTransferRequest({
signer,
endpoint: STARKNET_TESTNET,
fromAccount,
toAccount,
amount: "1.1",
});
const transferResult = await client.transfers.transfer(transferRequest);提现(Starknet)
import { createWithdrawalRequest } from "extended-sdk";
// onboarding 实例见上方 Onboarding 示例
const onboardingAccounts = await onboarding.getAccounts();
const account = onboardingAccounts[0]?.account;
if (!account) {
throw new Error("account not found");
}
const withdrawalRequest = await createWithdrawalRequest({
signer,
endpoint: STARKNET_TESTNET,
account,
amount: "2",
chainId: "STRK",
asset: "USD",
recipient: account.bridgeStarknetAddress ?? "0x0123", // 替换为实际 Starknet 地址
});
const withdrawalResult = await client.withdrawals.withdraw(withdrawalRequest);WebSocket 订阅(StreamClient)
import { StreamClient, STARKNET_MAINNET, WebSocketTransport } from "extended-sdk";
const transport = new WebSocketTransport({
baseUrl: STARKNET_MAINNET.streamBaseUrl,
reconnect: {
enabled: true,
maxAttempts: 10,
},
keepAlive: {
intervalMs: 30_000,
pongTimeoutMs: 10_000,
},
});
const stream = new StreamClient({
endpoint: STARKNET_MAINNET,
transport,
apiKey: process.env.EXTENDED_API_KEY,
});
const subscription = await stream.stream.publicTrades("ETH-USD", (message) => {
console.log(message);
});
process.on("SIGINT", async () => {
await subscription.unsubscribe();
});如需订阅账户相关推送(accountUpdates),必须提供 apiKey。
Orderbook Stream + 增量合并器
订单簿流是快照 + 增量(delta)模式,如果收到 seq 乱序请重连。
import {
StreamClient,
STARKNET_MAINNET,
WebSocketTransport,
OrderbookTracker,
} from "extended-sdk";
const transport = new WebSocketTransport({
baseUrl: STARKNET_MAINNET.streamBaseUrl,
});
const stream = new StreamClient({
endpoint: STARKNET_MAINNET,
transport,
});
const tracker = new OrderbookTracker();
const subscription = await stream.stream.orderbooks("ETH-USD", (message) => {
tracker.apply(message);
const snapshot = tracker.getSnapshot("ETH-USD");
if (snapshot) {
console.log(snapshot.bids[0], snapshot.asks[0]);
}
});
process.on("SIGINT", async () => {
await subscription.unsubscribe();
});端点配置
内置 STARKNET_MAINNET 与 STARKNET_TESTNET,也可以自行覆盖。apiBaseUrl 与 streamBaseUrl 仅填写协议+域名,SDK 会在请求时拼接 /api/v1 与 /stream.extended.exchange/v1 前缀:
import type { EndpointConfig } from "extended-sdk";
const customEndpoint: EndpointConfig = {
apiBaseUrl: "https://example",
streamBaseUrl: "wss://example",
onboardingBaseUrl: "https://example",
chainRpcUrl: "https://example/rpc",
signingDomain: "example",
collateralAssetId: "0x1",
collateralDecimals: 6,
};相关文档
- Extended API 说明:
Extended-API.md - 类型与 Schema:SDK 导出
schemas,用于直接复用响应与请求结构。
