npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@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)
  • zod v4:Schema 校验
  • ws:WebSocket 传输

请求头与错误处理

  • SDK 默认会设置 User-Agent<包名>/<版本>),可通过 userAgent 覆盖。
  • 当 REST 返回 HTTP 200 但响应 status=ERROR 时,会抛出 ApiError,并附带 code 与原始响应数据。

兼容性说明

  • getPositions 在部分环境会省略 maxPositionSizecreatedTimeupdatedTime,SDK 会将其解析为 undefined

安装

如果已发布到 npm:

npm install extended-sdk

从源码使用:

npm install
npm run build

npm 包仅包含 dist 构建产物,源码请查看仓库。

开发与测试

单元测试覆盖 onboarding 客户端关键流程。

npm run test

集成测试(Onboarding)

集成测试会真实调用 Extended API,默认跳过。请复制 .env.example.env 并填写:

  • EXTENDED_INTEGRATION_ENABLED=true
  • EXTENDED_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)

私有接口需要 apiKeysignersigner 用于生成订单相关的签名与密钥信息。

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_MAINNETSTARKNET_TESTNET,也可以自行覆盖。apiBaseUrlstreamBaseUrl 仅填写协议+域名,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,用于直接复用响应与请求结构。