mirror of
https://github.com/discountry/ritmex-bot.git
synced 2026-09-10 16:58:08 +00:00
feat(exchanges): add Ondo Perps exchange adapter
This commit is contained in:
@@ -88,6 +88,8 @@ function assignExchange(options: CliOptions, raw: string): void {
|
||||
options.exchange = normalized as CliOptions["exchange"];
|
||||
} else if (normalized === "gravity" || normalized === "grav" || normalized === "grv") {
|
||||
options.exchange = "grvt";
|
||||
} else if (normalized === "ondo" || normalized === "ondoperp") {
|
||||
options.exchange = "ondoperps";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -117,6 +117,15 @@ const STATIC_CAPABILITIES: Record<
|
||||
changeMarginMode: true,
|
||||
forceCancelAllOrders: true,
|
||||
},
|
||||
ondoperps: {
|
||||
trailingStops: false,
|
||||
fundingRate: true,
|
||||
precision: true,
|
||||
queryOpenOrders: true,
|
||||
queryAccountSnapshot: true,
|
||||
changeMarginMode: false,
|
||||
forceCancelAllOrders: true,
|
||||
},
|
||||
};
|
||||
|
||||
export interface CommandExecutorDependencies {
|
||||
|
||||
@@ -470,6 +470,7 @@ function normalizeExchange(value: string | undefined): SupportedExchangeId | und
|
||||
const normalized = value.trim().toLowerCase();
|
||||
if (normalized === "gravity" || normalized === "grav" || normalized === "grv") return "grvt";
|
||||
if (normalized === "bnb") return "binance";
|
||||
if (normalized === "ondo" || normalized === "ondoperp") return "ondoperps";
|
||||
if (isSupportedExchangeId(normalized)) return normalized;
|
||||
throw new CommandParseError(`Unsupported exchange '${value}'`);
|
||||
}
|
||||
|
||||
@@ -98,6 +98,7 @@ const SYMBOL_PRIORITY_BY_EXCHANGE: Record<SupportedExchangeId, { envKeys: string
|
||||
nado: { envKeys: ["NADO_SYMBOL", "TRADE_SYMBOL"], fallback: "BTC-PERP" },
|
||||
standx: { envKeys: ["STANDX_SYMBOL", "TRADE_SYMBOL"], fallback: "BTC-USD" },
|
||||
binance: { envKeys: ["BINANCE_SYMBOL", "TRADE_SYMBOL"], fallback: "BTCUSDT" },
|
||||
ondoperps: { envKeys: ["ONDOPERPS_SYMBOL", "ONDOPERP_SYMBOL", "TRADE_SYMBOL"], fallback: "BTC-USD.P" },
|
||||
};
|
||||
|
||||
export function resolveSymbolFromEnv(explicitExchangeId?: SupportedExchangeId | string | null): string {
|
||||
|
||||
@@ -7,6 +7,7 @@ import { ParadexExchangeAdapter, type ParadexCredentials } from "./paradex/adapt
|
||||
import { NadoExchangeAdapter, type NadoCredentials } from "./nado/adapter";
|
||||
import { StandxExchangeAdapter, type StandxCredentials } from "./standx/adapter";
|
||||
import { BinanceExchangeAdapter, type BinanceCredentials } from "./binance/adapter";
|
||||
import { OndoperpsExchangeAdapter, type OndoperpsCredentials } from "./ondoperps/adapter";
|
||||
|
||||
export const SUPPORTED_EXCHANGE_IDS = [
|
||||
"aster",
|
||||
@@ -17,6 +18,7 @@ export const SUPPORTED_EXCHANGE_IDS = [
|
||||
"nado",
|
||||
"standx",
|
||||
"binance",
|
||||
"ondoperps",
|
||||
] as const;
|
||||
|
||||
export const BASIS_SUPPORTED_EXCHANGE_IDS = [
|
||||
@@ -37,6 +39,8 @@ export interface ExchangeFactoryOptions {
|
||||
nado?: NadoCredentials;
|
||||
standx?: StandxCredentials;
|
||||
binance?: BinanceCredentials;
|
||||
ondoperps?: OndoperpsCredentials;
|
||||
ondoperp?: OndoperpsCredentials;
|
||||
}
|
||||
|
||||
export type SupportedExchangeId = (typeof SUPPORTED_EXCHANGE_IDS)[number];
|
||||
@@ -51,6 +55,7 @@ const EXCHANGE_DISPLAY_NAME: Record<SupportedExchangeId, string> = {
|
||||
nado: "Nado",
|
||||
standx: "StandX",
|
||||
binance: "Binance",
|
||||
ondoperps: "Ondo Perps",
|
||||
};
|
||||
|
||||
const EXCHANGE_ALIAS_MAP: Record<string, SupportedExchangeId> = {
|
||||
@@ -63,6 +68,9 @@ const EXCHANGE_ALIAS_MAP: Record<string, SupportedExchangeId> = {
|
||||
standx: "standx",
|
||||
binance: "binance",
|
||||
bnb: "binance",
|
||||
ondoperps: "ondoperps",
|
||||
ondoperp: "ondoperps",
|
||||
ondo: "ondoperps",
|
||||
};
|
||||
|
||||
export function isSupportedExchangeId(value: string): value is SupportedExchangeId {
|
||||
@@ -104,5 +112,10 @@ export function createExchangeAdapter(options: ExchangeFactoryOptions): Exchange
|
||||
return new StandxExchangeAdapter({ ...options.standx, symbol: options.symbol });
|
||||
case "binance":
|
||||
return new BinanceExchangeAdapter({ ...options.binance, symbol: options.symbol });
|
||||
case "ondoperps":
|
||||
return new OndoperpsExchangeAdapter({
|
||||
...(options.ondoperps ?? options.ondoperp),
|
||||
symbol: options.symbol,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
import type {
|
||||
AccountListener,
|
||||
ConnectionEventListener,
|
||||
DepthListener,
|
||||
ExchangeAdapter,
|
||||
ExchangePrecision,
|
||||
FundingRateListener,
|
||||
KlineListener,
|
||||
OrderListener,
|
||||
TickerListener,
|
||||
} from "../adapter";
|
||||
import { createInitManager, createSafeInvoke } from "../adapter-utils";
|
||||
import type { CreateOrderParams, Order } from "../types";
|
||||
import { OndoperpsGateway, type OndoperpsGatewayOptions } from "./gateway";
|
||||
|
||||
export interface OndoperpsCredentials {
|
||||
apiKeyId?: string;
|
||||
apiSecret?: string;
|
||||
symbol?: string;
|
||||
baseUrl?: string;
|
||||
wsUrl?: string;
|
||||
builderCode?: string;
|
||||
builderFeeRateBps?: number;
|
||||
logger?: OndoperpsGatewayOptions["logger"];
|
||||
}
|
||||
|
||||
export class OndoperpsExchangeAdapter implements ExchangeAdapter {
|
||||
readonly id = "ondoperps";
|
||||
|
||||
private readonly gateway: OndoperpsGateway;
|
||||
private readonly symbol: string;
|
||||
private readonly safeInvoke = createSafeInvoke("OndoperpsExchangeAdapter");
|
||||
private readonly init: ReturnType<typeof createInitManager>;
|
||||
|
||||
constructor(credentials: OndoperpsCredentials = {}) {
|
||||
const apiKeyId = credentials.apiKeyId ?? process.env.ONDOPERPS_API_KEY_ID ?? process.env.ONDOPERP_API_KEY_ID ?? process.env.ONDO_KEY_ID;
|
||||
const apiSecret = credentials.apiSecret ?? process.env.ONDOPERPS_API_SECRET ?? process.env.ONDOPERP_API_SECRET ?? process.env.ONDO_API_SECRET;
|
||||
if (!apiKeyId || !apiSecret) {
|
||||
throw new Error("Missing ONDOPERPS_API_KEY_ID or ONDOPERPS_API_SECRET environment variable");
|
||||
}
|
||||
this.symbol = credentials.symbol ?? process.env.ONDOPERPS_SYMBOL ?? process.env.ONDOPERP_SYMBOL ?? process.env.TRADE_SYMBOL ?? "BTC-USD.P";
|
||||
const sandbox = parseBoolean(process.env.ONDOPERPS_SANDBOX ?? process.env.ONDOPERP_SANDBOX);
|
||||
this.gateway = new OndoperpsGateway({
|
||||
apiKeyId,
|
||||
apiSecret,
|
||||
symbol: this.symbol,
|
||||
baseUrl: credentials.baseUrl ?? process.env.ONDOPERPS_BASE_URL ?? process.env.ONDOPERP_BASE_URL ?? (sandbox ? "https://api.ondoperps-sandbox.xyz" : undefined),
|
||||
wsUrl: credentials.wsUrl ?? process.env.ONDOPERPS_WS_URL ?? process.env.ONDOPERP_WS_URL ?? (sandbox ? "wss://api.ondoperps-sandbox.xyz/ws" : undefined),
|
||||
builderCode: credentials.builderCode ?? process.env.ONDOPERPS_BUILDER_CODE ?? process.env.ONDOPERP_BUILDER_CODE,
|
||||
builderFeeRateBps: credentials.builderFeeRateBps ?? parseNumber(
|
||||
process.env.ONDOPERPS_BUILDER_FEE_RATE_BPS ?? process.env.ONDOPERP_BUILDER_FEE_RATE_BPS,
|
||||
),
|
||||
logger: credentials.logger,
|
||||
});
|
||||
this.init = createInitManager("OndoperpsExchangeAdapter", () => this.gateway.ensureInitialized());
|
||||
}
|
||||
|
||||
supportsTrailingStops(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
watchAccount(cb: AccountListener): void {
|
||||
void this.init.ensureInitialized("watchAccount");
|
||||
this.gateway.onAccount(this.safeInvoke("watchAccount", cb));
|
||||
}
|
||||
|
||||
watchOrders(cb: OrderListener): void {
|
||||
void this.init.ensureInitialized("watchOrders");
|
||||
this.gateway.onOrders(this.safeInvoke("watchOrders", cb));
|
||||
}
|
||||
|
||||
watchDepth(symbol: string, cb: DepthListener): void {
|
||||
void this.init.ensureInitialized("watchDepth");
|
||||
this.gateway.onDepth(symbol, this.safeInvoke("watchDepth", cb));
|
||||
}
|
||||
|
||||
watchTicker(symbol: string, cb: TickerListener): void {
|
||||
void this.init.ensureInitialized("watchTicker");
|
||||
this.gateway.onTicker(symbol, this.safeInvoke("watchTicker", cb));
|
||||
}
|
||||
|
||||
watchKlines(symbol: string, interval: string, cb: KlineListener): void {
|
||||
void this.init.ensureInitialized("watchKlines");
|
||||
this.gateway.onKlines(symbol, interval, this.safeInvoke("watchKlines", cb));
|
||||
}
|
||||
|
||||
watchFundingRate(symbol: string, cb: FundingRateListener): void {
|
||||
void this.init.ensureInitialized("watchFundingRate");
|
||||
this.gateway.onFundingRate(symbol, this.safeInvoke("watchFundingRate", cb));
|
||||
}
|
||||
|
||||
async createOrder(params: CreateOrderParams): Promise<Order> {
|
||||
await this.init.ensureInitialized("createOrder");
|
||||
return this.gateway.createOrder(params);
|
||||
}
|
||||
|
||||
async cancelOrder(params: { symbol: string; orderId: number | string }): Promise<void> {
|
||||
await this.init.ensureInitialized("cancelOrder");
|
||||
await this.gateway.cancelOrder(params);
|
||||
}
|
||||
|
||||
async cancelOrders(params: { symbol: string; orderIdList: Array<number | string> }): Promise<void> {
|
||||
await this.init.ensureInitialized("cancelOrders");
|
||||
await this.gateway.cancelOrders(params);
|
||||
}
|
||||
|
||||
async cancelAllOrders(params: { symbol: string }): Promise<void> {
|
||||
await this.init.ensureInitialized("cancelAllOrders");
|
||||
await this.gateway.cancelAllOrders(params);
|
||||
}
|
||||
|
||||
async getPrecision(): Promise<ExchangePrecision | null> {
|
||||
await this.init.ensureInitialized("getPrecision");
|
||||
return this.gateway.getPrecision(this.symbol);
|
||||
}
|
||||
|
||||
onConnectionEvent(listener: ConnectionEventListener): void {
|
||||
this.gateway.onConnectionEvent(listener);
|
||||
}
|
||||
|
||||
offConnectionEvent(listener: ConnectionEventListener): void {
|
||||
this.gateway.offConnectionEvent(listener);
|
||||
}
|
||||
|
||||
async queryOpenOrders(): Promise<Order[]> {
|
||||
await this.init.ensureInitialized("queryOpenOrders");
|
||||
return this.gateway.queryOpenOrders();
|
||||
}
|
||||
|
||||
async queryAccountSnapshot() {
|
||||
await this.init.ensureInitialized("queryAccountSnapshot");
|
||||
return this.gateway.queryAccountSnapshot();
|
||||
}
|
||||
|
||||
async forceCancelAllOrders(): Promise<boolean> {
|
||||
await this.init.ensureInitialized("forceCancelAllOrders");
|
||||
return this.gateway.forceCancelAllOrders();
|
||||
}
|
||||
}
|
||||
|
||||
function parseBoolean(value: string | undefined): boolean {
|
||||
if (!value) return false;
|
||||
return ["1", "true", "yes", "on"].includes(value.trim().toLowerCase());
|
||||
}
|
||||
|
||||
function parseNumber(value: string | undefined): number | undefined {
|
||||
if (!value) return undefined;
|
||||
const number = Number(value);
|
||||
return Number.isFinite(number) ? number : undefined;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,23 @@
|
||||
import { createOrderHandlers } from "../order-handlers";
|
||||
|
||||
const handlers = createOrderHandlers({
|
||||
exchangeName: "Ondo Perps",
|
||||
defaultLimitTimeInForce: "GTX",
|
||||
defaultMarketTimeInForce: "IOC",
|
||||
defaultCloseTimeInForce: "IOC",
|
||||
defaultStopTimeInForce: "GTC",
|
||||
defaultStopTriggerType: "STOP_LOSS",
|
||||
supportsTrailingStop: false,
|
||||
supportsTriggerType: true,
|
||||
stopDefaultReduceOnly: true,
|
||||
stopDefaultClosePosition: true,
|
||||
closeDefaultClosePosition: true,
|
||||
});
|
||||
|
||||
export const {
|
||||
createLimitOrder,
|
||||
createMarketOrder,
|
||||
createStopOrder,
|
||||
createTrailingStopOrder,
|
||||
createClosePositionOrder,
|
||||
} = handlers;
|
||||
@@ -0,0 +1,173 @@
|
||||
export interface OndoperpsApiResponse<T> {
|
||||
success: boolean;
|
||||
result?: T;
|
||||
error?: string;
|
||||
error_code?: string;
|
||||
}
|
||||
|
||||
export interface OndoperpsOrder {
|
||||
orderId: string;
|
||||
clientOrderId?: string;
|
||||
parentOrderId?: string;
|
||||
side: "buy" | "sell";
|
||||
price?: string;
|
||||
size: string;
|
||||
market: string;
|
||||
filledSize?: string;
|
||||
lastFillSize?: string;
|
||||
filledCost?: string;
|
||||
realizedPnl?: string;
|
||||
fee?: string;
|
||||
feeRebate?: string;
|
||||
status: "open" | "fullyfilled" | "canceled" | "pending" | "untriggered" | string;
|
||||
createdAt: string;
|
||||
filledAt?: string;
|
||||
canceledAt?: string;
|
||||
cancelReason?: string;
|
||||
type: "limit" | "market" | "stopMarket" | "takeProfitMarket" | string;
|
||||
timeInForce?: "GTC" | "IOC" | "FOK";
|
||||
reduceOnly?: boolean;
|
||||
closePosition?: boolean;
|
||||
stopOrderType?: "stopLoss" | "takeProfit";
|
||||
triggerPrice?: string;
|
||||
}
|
||||
|
||||
export interface OndoperpsPosition {
|
||||
market: string;
|
||||
direction: "long" | "short" | "neutral";
|
||||
netQuantity: string;
|
||||
averageEntryPrice: string;
|
||||
usedMargin: string;
|
||||
unrealizedPnl: string;
|
||||
markPrice: string;
|
||||
liquidationPrice: string;
|
||||
bankruptcyPrice: string;
|
||||
maintenanceMargin: string;
|
||||
notionalValue: string;
|
||||
leverage: string;
|
||||
netFundingSinceNeutral: string;
|
||||
returnOnEquity: string;
|
||||
stopLossTriggerPrice?: string;
|
||||
takeProfitTriggerPrice?: string;
|
||||
}
|
||||
|
||||
export interface OndoperpsBalance {
|
||||
walletBalance: string;
|
||||
realizedPnl: string;
|
||||
unrealizedPnl: string;
|
||||
marginBalance: string;
|
||||
usedMargin: string;
|
||||
availableMargin: string;
|
||||
withdrawableMargin: string;
|
||||
maintenanceMarginRequirement: string;
|
||||
totalMaintenanceMargin: string;
|
||||
marginRatio: string;
|
||||
leverage: string;
|
||||
underLiquidation: boolean;
|
||||
totalFundingPayments: string;
|
||||
totalTradingFees: string;
|
||||
totalPnL: string;
|
||||
netInvested?: string;
|
||||
}
|
||||
|
||||
export interface OndoperpsStopOrders {
|
||||
market: string;
|
||||
positionDirection: "long" | "short" | "neutral";
|
||||
stopLoss?: string | null;
|
||||
takeProfit?: string | null;
|
||||
}
|
||||
|
||||
export type OndoperpsBookLevel = [string, string];
|
||||
|
||||
export interface OndoperpsBookSnapshot {
|
||||
market: string;
|
||||
time: string;
|
||||
bids: OndoperpsBookLevel[];
|
||||
asks: OndoperpsBookLevel[];
|
||||
depthLevels?: string;
|
||||
}
|
||||
|
||||
export interface OndoperpsCandle {
|
||||
startTime: string;
|
||||
open: string;
|
||||
high: string;
|
||||
low: string;
|
||||
close: string;
|
||||
volume: string;
|
||||
}
|
||||
|
||||
export interface OndoperpsWsKline {
|
||||
m: string;
|
||||
t: number;
|
||||
s: number;
|
||||
e: number;
|
||||
o: number;
|
||||
h: number;
|
||||
l: number;
|
||||
c: number;
|
||||
v: number;
|
||||
x?: boolean;
|
||||
}
|
||||
|
||||
export interface OndoperpsContract {
|
||||
market: string;
|
||||
displayName?: string;
|
||||
productType: string;
|
||||
contractType: string;
|
||||
baseCurrency: string;
|
||||
quoteCurrency: string;
|
||||
disabled: boolean;
|
||||
lastPrice?: string;
|
||||
baseVolume?: string;
|
||||
quoteVolume?: string;
|
||||
usdVolume?: string;
|
||||
bid?: string;
|
||||
ask?: string;
|
||||
high?: string;
|
||||
low?: string;
|
||||
openInterest?: string;
|
||||
openInterestUsd?: string;
|
||||
indexPrice?: string;
|
||||
fundingRate?: string;
|
||||
nextFundingRate?: string;
|
||||
nextFundingRateTimestamp?: string;
|
||||
makerFee?: string;
|
||||
takerFee?: string;
|
||||
priceChangePercent?: string;
|
||||
isClosed?: boolean;
|
||||
}
|
||||
|
||||
export interface OndoperpsMarkPrice {
|
||||
market: string;
|
||||
price: string;
|
||||
markPrice: string;
|
||||
oraclePrice?: string;
|
||||
lastExternalPrice?: string;
|
||||
lastUpdatedTime?: string;
|
||||
}
|
||||
|
||||
export interface OndoperpsFundingRate {
|
||||
market: string;
|
||||
rate: string;
|
||||
intervalEnds?: string;
|
||||
}
|
||||
|
||||
export interface OndoperpsTradingPair {
|
||||
market: string;
|
||||
baseIncrement: string;
|
||||
quoteIncrement: string;
|
||||
}
|
||||
|
||||
export interface OndoperpsMarketsResult {
|
||||
perps?: {
|
||||
tradingPairs?: OndoperpsTradingPair[];
|
||||
};
|
||||
}
|
||||
|
||||
export interface OndoperpsWsMessage {
|
||||
type: "pong" | "loggedIn" | "subscribed" | "unsubscribed" | "update" | "error" | string;
|
||||
channel?: string;
|
||||
code?: number;
|
||||
msg?: string;
|
||||
data?: unknown;
|
||||
}
|
||||
@@ -17,6 +17,7 @@ import * as paradexOrders from "./paradex/order";
|
||||
import * as nadoOrders from "./nado/order";
|
||||
import * as standxOrders from "./standx/order";
|
||||
import * as binanceOrders from "./binance/order";
|
||||
import * as ondoperpsOrders from "./ondoperps/order";
|
||||
|
||||
type ExchangeKey = SupportedExchangeId;
|
||||
|
||||
@@ -85,13 +86,21 @@ const handlerMap: Record<ExchangeKey, ExchangeOrderHandlers> = {
|
||||
trailingStop: binanceOrders.createTrailingStopOrder,
|
||||
close: binanceOrders.createClosePositionOrder,
|
||||
},
|
||||
ondoperps: {
|
||||
limit: ondoperpsOrders.createLimitOrder,
|
||||
market: ondoperpsOrders.createMarketOrder,
|
||||
stop: ondoperpsOrders.createStopOrder,
|
||||
trailingStop: ondoperpsOrders.createTrailingStopOrder,
|
||||
close: ondoperpsOrders.createClosePositionOrder,
|
||||
},
|
||||
};
|
||||
|
||||
const knownExchanges: ExchangeKey[] = [...SUPPORTED_EXCHANGE_IDS];
|
||||
|
||||
function normalizeExchangeId(value: string | undefined | null): string | undefined {
|
||||
if (!value) return undefined;
|
||||
return value.trim().toLowerCase();
|
||||
const normalized = value.trim().toLowerCase();
|
||||
return normalized === "ondoperp" ? "ondoperps" : normalized;
|
||||
}
|
||||
|
||||
function resolveExchangeKey(adapter: ExchangeAdapter): ExchangeKey {
|
||||
|
||||
@@ -7,6 +7,7 @@ import type { ParadexCredentials } from "./paradex/adapter";
|
||||
import type { NadoCredentials } from "./nado/adapter";
|
||||
import type { StandxCredentials } from "./standx/adapter";
|
||||
import type { BinanceCredentials } from "./binance/adapter";
|
||||
import type { OndoperpsCredentials } from "./ondoperps/adapter";
|
||||
import { t } from "../i18n";
|
||||
import type { Address } from "viem";
|
||||
|
||||
@@ -50,6 +51,10 @@ export function buildAdapterFromEnv(options: BuildAdapterOptions): ExchangeAdapt
|
||||
const credentials = resolveBinanceCredentials(symbol);
|
||||
return createExchangeAdapter({ exchange: id, symbol, binance: credentials });
|
||||
}
|
||||
case "ondoperps": {
|
||||
const credentials = resolveOndoperpsCredentials(symbol);
|
||||
return createExchangeAdapter({ exchange: id, symbol, ondoperps: credentials });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,6 +205,26 @@ function resolveBinanceCredentials(symbol: string): BinanceCredentials {
|
||||
};
|
||||
}
|
||||
|
||||
function resolveOndoperpsCredentials(symbol: string): OndoperpsCredentials {
|
||||
const apiKeyId = process.env.ONDOPERPS_API_KEY_ID ?? process.env.ONDOPERP_API_KEY_ID ?? process.env.ONDO_KEY_ID;
|
||||
const apiSecret = process.env.ONDOPERPS_API_SECRET ?? process.env.ONDOPERP_API_SECRET ?? process.env.ONDO_API_SECRET;
|
||||
if (!apiKeyId || !apiSecret) {
|
||||
throw new Error(t("env.missingOndoperps"));
|
||||
}
|
||||
const sandbox = parseOptionalBoolean(process.env.ONDOPERPS_SANDBOX ?? process.env.ONDOPERP_SANDBOX) === true;
|
||||
return {
|
||||
apiKeyId,
|
||||
apiSecret,
|
||||
symbol: process.env.ONDOPERPS_SYMBOL ?? process.env.ONDOPERP_SYMBOL ?? symbol,
|
||||
baseUrl: process.env.ONDOPERPS_BASE_URL ?? process.env.ONDOPERP_BASE_URL ?? (sandbox ? "https://api.ondoperps-sandbox.xyz" : undefined),
|
||||
wsUrl: process.env.ONDOPERPS_WS_URL ?? process.env.ONDOPERP_WS_URL ?? (sandbox ? "wss://api.ondoperps-sandbox.xyz/ws" : undefined),
|
||||
builderCode: process.env.ONDOPERPS_BUILDER_CODE ?? process.env.ONDOPERP_BUILDER_CODE ?? undefined,
|
||||
builderFeeRateBps: parseOptionalNumber(
|
||||
process.env.ONDOPERPS_BUILDER_FEE_RATE_BPS ?? process.env.ONDOPERP_BUILDER_FEE_RATE_BPS,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
function isHex32(value: string): boolean {
|
||||
return /^0x[0-9a-fA-F]{64}$/.test(value.trim());
|
||||
}
|
||||
|
||||
@@ -426,6 +426,10 @@ const translations: Record<string, TranslationEntry> = {
|
||||
zh: "StandX 需要配置 STANDX_TOKEN",
|
||||
en: "StandX requires STANDX_TOKEN",
|
||||
},
|
||||
"env.missingOndoperps": {
|
||||
zh: "Ondo Perps 需要配置 ONDOPERPS_API_KEY_ID 与 ONDOPERPS_API_SECRET(兼容旧 ONDOPERP_ 前缀)",
|
||||
en: "Ondo Perps requires ONDOPERPS_API_KEY_ID and ONDOPERPS_API_SECRET (legacy ONDOPERP_ prefix is supported)",
|
||||
},
|
||||
"log.subscribe.accountFail": {
|
||||
zh: "订阅账户失败: {error}",
|
||||
en: "Failed to subscribe account: {error}",
|
||||
|
||||
Reference in New Issue
Block a user