@usherlabs/cex-broker 0.2.9 → 0.2.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -272748,16 +272748,18 @@ if (process.env.LOG_LEVEL !== "debug") {
272748
272748
  var log = baseLogger;
272749
272749
 
272750
272750
  // src/helpers/index.ts
272751
- class WithdrawRoutingError extends Error {
272752
- }
272753
-
272754
- class WithdrawRoutingUnavailableError extends WithdrawRoutingError {
272755
- }
272756
- function isMasterBrokerAccount(account) {
272757
- return account.label === "primary" || account.role === "master";
272751
+ class BrokerAccountPreconditionError extends Error {
272752
+ constructor(message) {
272753
+ super(message);
272754
+ this.name = "BrokerAccountPreconditionError";
272755
+ }
272758
272756
  }
272759
- function isSubaccountBrokerAccount(account) {
272760
- return !isMasterBrokerAccount(account);
272757
+ function requireDestinationEmail(dest, transferType) {
272758
+ const email = dest.email?.trim();
272759
+ if (!email) {
272760
+ throw new BrokerAccountPreconditionError(`Destination account '${dest.label}' requires an email configured for ${transferType} transfers`);
272761
+ }
272762
+ return email;
272761
272763
  }
272762
272764
  function authenticateRequest(call, whitelistIps) {
272763
272765
  const clientIp = call.getPeer().split(":")[0];
@@ -273079,81 +273081,50 @@ function validateWithdraw(policy, exchange, network, recipientAddress, _amount,
273079
273081
  }
273080
273082
  return { valid: true };
273081
273083
  }
273082
- function normalizeAccountSelector(selector2, metadata, defaultSelector) {
273083
- const raw = selector2?.trim().toLowerCase() ?? defaultSelector;
273084
- if (raw === "current") {
273085
- return getCurrentBrokerSelector(metadata);
273086
- }
273087
- if (raw === "primary") {
273088
- return "primary";
273089
- }
273090
- const secondaryMatch = raw.match(/^secondary:(\d+)$/);
273091
- if (secondaryMatch) {
273092
- return `secondary:${secondaryMatch[1]}`;
273093
- }
273094
- throw new WithdrawRoutingError(`Invalid account selector "${selector2}"`);
273095
- }
273096
- async function transferBinanceSubAccountToMaster(source, code, amount) {
273084
+ async function transferBinanceInternal(source, dest, code, amount) {
273097
273085
  const exchange = source.exchange;
273098
- if (typeof exchange.sapiPostSubAccountTransferSubToMaster !== "function") {
273099
- throw new WithdrawRoutingUnavailableError("Binance sub-account to master transfer is unavailable in this CCXT build");
273100
- }
273101
273086
  await source.exchange.loadMarkets();
273102
273087
  const currency = source.exchange.currency(code);
273103
- return await exchange.sapiPostSubAccountTransferSubToMaster({
273104
- asset: currency.id,
273105
- amount: source.exchange.currencyToPrecision(code, amount)
273106
- });
273107
- }
273108
- async function executeWithdrawWithRouting(args) {
273109
- const {
273110
- cex: cex3,
273111
- brokers,
273112
- metadata,
273113
- selectedBroker,
273114
- code,
273115
- amount,
273116
- recipientAddress,
273117
- network,
273118
- params,
273119
- routeViaMaster,
273120
- sourceAccount,
273121
- masterAccount
273122
- } = args;
273123
- const withdrawParams = {
273124
- ...params ?? {},
273125
- network
273126
- };
273127
- if (!routeViaMaster) {
273128
- return await selectedBroker.withdraw(code, amount, recipientAddress, undefined, withdrawParams);
273129
- }
273130
- const sourceSelector = normalizeAccountSelector(sourceAccount, metadata, "current");
273131
- const masterSelector = normalizeAccountSelector(masterAccount, metadata, "primary");
273132
- if (!brokers) {
273133
- throw new WithdrawRoutingUnavailableError("Routed withdraw requires configured broker accounts");
273134
- }
273135
- const source = resolveBrokerAccount(brokers, sourceSelector);
273136
- if (!source) {
273137
- throw new WithdrawRoutingError(`Source account ${sourceSelector} is not configured`);
273138
- }
273139
- const master = resolveBrokerAccount(brokers, masterSelector);
273140
- if (!master) {
273141
- throw new WithdrawRoutingError(`Master account ${masterSelector} is not configured`);
273142
- }
273143
- if (!isMasterBrokerAccount(master)) {
273144
- throw new WithdrawRoutingError(`Master account ${masterSelector} must resolve to the primary/master account`);
273145
- }
273146
- if (source.label === master.label) {
273147
- return await master.exchange.withdraw(code, amount, recipientAddress, undefined, withdrawParams);
273088
+ const asset = currency.id;
273089
+ const amountStr = source.exchange.currencyToPrecision(code, amount);
273090
+ const isSourceSecondary = source.label.startsWith("secondary:");
273091
+ const isDestPrimary = dest.label === "primary";
273092
+ const isDestSecondary = dest.label.startsWith("secondary:");
273093
+ const isSourcePrimary = source.label === "primary";
273094
+ if (isSourceSecondary && isDestPrimary) {
273095
+ if (typeof exchange.sapiPostSubAccountTransferSubToMaster !== "function") {
273096
+ throw new Error("Binance sub\u2192master transfer is unavailable in this CCXT build");
273097
+ }
273098
+ return await exchange.sapiPostSubAccountTransferSubToMaster({
273099
+ asset,
273100
+ amount: amountStr
273101
+ });
273148
273102
  }
273149
- if (!isSubaccountBrokerAccount(source)) {
273150
- throw new WithdrawRoutingError(`Source account ${sourceSelector} must resolve to a subaccount when routeViaMaster is enabled`);
273103
+ if (isSourceSecondary && isDestSecondary) {
273104
+ if (typeof exchange.sapiPostSubAccountTransferSubToSub !== "function") {
273105
+ throw new Error("Binance sub\u2192sub transfer is unavailable in this CCXT build");
273106
+ }
273107
+ const destEmail = requireDestinationEmail(dest, "sub-to-sub");
273108
+ return await exchange.sapiPostSubAccountTransferSubToSub({
273109
+ toEmail: destEmail,
273110
+ asset,
273111
+ amount: amountStr
273112
+ });
273151
273113
  }
273152
- if (cex3.trim().toLowerCase() !== "binance") {
273153
- throw new WithdrawRoutingUnavailableError(`Withdraw routing via master is not supported for ${cex3}`);
273114
+ if (isSourcePrimary && isDestSecondary) {
273115
+ if (typeof exchange.sapiPostSubAccountUniversalTransfer !== "function") {
273116
+ throw new Error("Binance universal transfer is unavailable in this CCXT build");
273117
+ }
273118
+ const destEmail = requireDestinationEmail(dest, "primary-to-sub");
273119
+ return await exchange.sapiPostSubAccountUniversalTransfer({
273120
+ fromAccountType: "SPOT",
273121
+ toAccountType: "SPOT",
273122
+ toEmail: destEmail,
273123
+ asset,
273124
+ amount: amountStr
273125
+ });
273154
273126
  }
273155
- await transferBinanceSubAccountToMaster(source, code, amount);
273156
- return await master.exchange.withdraw(code, amount, recipientAddress, undefined, withdrawParams);
273127
+ throw new Error(`Unsupported transfer direction: ${source.label} \u2192 ${dest.label}`);
273157
273128
  }
273158
273129
  function isMarketPatternMatch(pattern, broker, fromToken, toToken) {
273159
273130
  const normalizedPattern = pattern.toUpperCase().trim();
@@ -277336,7 +277307,8 @@ var Action = {
277336
277307
  FetchCurrency: 9,
277337
277308
  Call: 10,
277338
277309
  FetchAccountId: 11,
277339
- FetchFees: 12
277310
+ FetchFees: 12,
277311
+ InternalTransfer: 13
277340
277312
  };
277341
277313
 
277342
277314
  // src/proto/cex_broker/SubscriptionType.ts
@@ -277467,7 +277439,8 @@ var descriptor = {
277467
277439
  FetchCurrency: 9,
277468
277440
  Call: 10,
277469
277441
  FetchAccountId: 11,
277470
- FetchFees: 12
277442
+ FetchFees: 12,
277443
+ InternalTransfer: 13
277471
277444
  }
277472
277445
  }
277473
277446
  }
@@ -291053,11 +291026,13 @@ var WithdrawPayloadSchema = exports_external.object({
291053
291026
  recipientAddress: exports_external.string().min(1),
291054
291027
  amount: exports_external.coerce.number().positive(),
291055
291028
  chain: exports_external.string().min(1),
291056
- routeViaMaster: booleanLikeSchema.optional().default(false),
291057
- sourceAccount: exports_external.string().min(1).optional(),
291058
- masterAccount: exports_external.string().min(1).optional(),
291059
291029
  params: exports_external.preprocess(parseJsonString, stringNumberRecordSchema).default({})
291060
291030
  });
291031
+ var InternalTransferPayloadSchema = exports_external.object({
291032
+ amount: exports_external.coerce.number().positive(),
291033
+ fromAccount: exports_external.string().min(1).optional(),
291034
+ toAccount: exports_external.string().min(1).optional()
291035
+ });
291061
291036
  var CreateOrderPayloadSchema = exports_external.object({
291062
291037
  orderType: exports_external.enum(["market", "limit"]).default("limit"),
291063
291038
  amount: exports_external.coerce.number().positive(),
@@ -291105,6 +291080,31 @@ function safeLogError(context3, error48) {
291105
291080
  console.error(context3, error48);
291106
291081
  }
291107
291082
  }
291083
+ function mapCcxtErrorToGrpcStatus(error48) {
291084
+ if (error48 instanceof ccxt_default.AuthenticationError)
291085
+ return grpc.status.UNAUTHENTICATED;
291086
+ if (error48 instanceof ccxt_default.PermissionDenied)
291087
+ return grpc.status.PERMISSION_DENIED;
291088
+ if (error48 instanceof ccxt_default.InsufficientFunds)
291089
+ return grpc.status.FAILED_PRECONDITION;
291090
+ if (error48 instanceof ccxt_default.InvalidAddress)
291091
+ return grpc.status.INVALID_ARGUMENT;
291092
+ if (error48 instanceof ccxt_default.BadSymbol)
291093
+ return grpc.status.NOT_FOUND;
291094
+ if (error48 instanceof ccxt_default.BadRequest)
291095
+ return grpc.status.INVALID_ARGUMENT;
291096
+ if (error48 instanceof ccxt_default.NotSupported)
291097
+ return grpc.status.UNIMPLEMENTED;
291098
+ if (error48 instanceof ccxt_default.RateLimitExceeded)
291099
+ return grpc.status.RESOURCE_EXHAUSTED;
291100
+ if (error48 instanceof ccxt_default.OnMaintenance)
291101
+ return grpc.status.UNAVAILABLE;
291102
+ if (error48 instanceof ccxt_default.ExchangeNotAvailable)
291103
+ return grpc.status.UNAVAILABLE;
291104
+ if (error48 instanceof ccxt_default.NetworkError)
291105
+ return grpc.status.UNAVAILABLE;
291106
+ return;
291107
+ }
291108
291108
  function getServer(policy, brokers, whitelistIps, useVerity, verityProverUrl, otelMetrics) {
291109
291109
  const server = new grpc.Server;
291110
291110
  server.addService(cexNode.cex_service.service, {
@@ -291160,7 +291160,8 @@ function getServer(policy, brokers, whitelistIps, useVerity, verityProverUrl, ot
291160
291160
  message: "`action` AND `cex` fields are required"
291161
291161
  }, null);
291162
291162
  }
291163
- const broker = selectBroker(brokers[cex3], metadata) ?? createBroker(cex3, metadata);
291163
+ const normalizedCex = cex3.trim().toLowerCase();
291164
+ const broker = selectBroker(brokers[normalizedCex], metadata) ?? createBroker(normalizedCex, metadata);
291164
291165
  if (!broker) {
291165
291166
  return wrappedCallback({
291166
291167
  code: grpc.status.UNAUTHENTICATED,
@@ -291498,39 +291499,10 @@ function getServer(policy, brokers, whitelistIps, useVerity, verityProverUrl, ot
291498
291499
  }, null);
291499
291500
  }
291500
291501
  try {
291501
- let transaction;
291502
- try {
291503
- transaction = await executeWithdrawWithRouting({
291504
- cex: cex3,
291505
- brokers: brokers[cex3],
291506
- metadata,
291507
- selectedBroker: broker,
291508
- code: symbol2,
291509
- amount: transferValue.amount,
291510
- recipientAddress: transferValue.recipientAddress,
291511
- network: transferValue.chain,
291512
- params: transferValue.params,
291513
- routeViaMaster: transferValue.routeViaMaster,
291514
- sourceAccount: transferValue.sourceAccount,
291515
- masterAccount: transferValue.masterAccount
291516
- });
291517
- } catch (error48) {
291518
- if (error48 instanceof WithdrawRoutingUnavailableError) {
291519
- log.warn("Withdraw routing unavailable, falling back", {
291520
- cex: cex3,
291521
- error: error48.message
291522
- });
291523
- if (transferValue.routeViaMaster) {
291524
- throw error48;
291525
- }
291526
- transaction = await broker.withdraw(symbol2, transferValue.amount, transferValue.recipientAddress, undefined, {
291527
- ...transferValue.params ?? {},
291528
- network: transferValue.chain
291529
- });
291530
- } else {
291531
- throw error48;
291532
- }
291533
- }
291502
+ const transaction = await broker.withdraw(symbol2, transferValue.amount, transferValue.recipientAddress, undefined, {
291503
+ ...transferValue.params ?? {},
291504
+ network: transferValue.chain
291505
+ });
291534
291506
  log.info(`Withdraw Result: ${JSON.stringify(transaction)}`);
291535
291507
  wrappedCallback(null, {
291536
291508
  proof: verityProof,
@@ -291538,10 +291510,10 @@ function getServer(policy, brokers, whitelistIps, useVerity, verityProverUrl, ot
291538
291510
  });
291539
291511
  } catch (error48) {
291540
291512
  safeLogError("Withdraw failed", error48);
291541
- const message = error48 instanceof WithdrawRoutingError ? error48.message : getErrorMessage(error48);
291513
+ const code = mapCcxtErrorToGrpcStatus(error48) ?? grpc.status.INTERNAL;
291542
291514
  wrappedCallback({
291543
- code: error48 instanceof WithdrawRoutingError ? grpc.status.INVALID_ARGUMENT : grpc.status.INTERNAL,
291544
- message: `Withdraw failed: ${message}`
291515
+ code,
291516
+ message: `Withdraw failed: ${getErrorMessage(error48)}`
291545
291517
  }, null);
291546
291518
  }
291547
291519
  break;
@@ -291714,6 +291686,86 @@ function getServer(policy, brokers, whitelistIps, useVerity, verityProverUrl, ot
291714
291686
  }, null);
291715
291687
  }
291716
291688
  break;
291689
+ case Action.InternalTransfer: {
291690
+ if (!symbol2) {
291691
+ return wrappedCallback({
291692
+ code: grpc.status.INVALID_ARGUMENT,
291693
+ message: `ValidationError: Symbol required`
291694
+ }, null);
291695
+ }
291696
+ const parsedPayload = parsePayload(InternalTransferPayloadSchema, call.request.payload);
291697
+ if (!parsedPayload.success) {
291698
+ return wrappedCallback({
291699
+ code: grpc.status.INVALID_ARGUMENT,
291700
+ message: parsedPayload.message
291701
+ }, null);
291702
+ }
291703
+ const transferPayload = parsedPayload.data;
291704
+ if (normalizedCex !== "binance") {
291705
+ return wrappedCallback({
291706
+ code: grpc.status.UNIMPLEMENTED,
291707
+ message: `InternalTransfer is only supported for Binance`
291708
+ }, null);
291709
+ }
291710
+ const pool = brokers[normalizedCex];
291711
+ if (!pool) {
291712
+ return wrappedCallback({
291713
+ code: grpc.status.FAILED_PRECONDITION,
291714
+ message: `No broker accounts configured for ${normalizedCex}`
291715
+ }, null);
291716
+ }
291717
+ const fromSelector = transferPayload.fromAccount ?? getCurrentBrokerSelector(metadata);
291718
+ const toSelector = transferPayload.toAccount ?? "primary";
291719
+ const sourceAccount = resolveBrokerAccount(pool, fromSelector);
291720
+ if (!sourceAccount) {
291721
+ return wrappedCallback({
291722
+ code: grpc.status.INVALID_ARGUMENT,
291723
+ message: `Source account "${fromSelector}" is not configured`
291724
+ }, null);
291725
+ }
291726
+ const destAccount = resolveBrokerAccount(pool, toSelector);
291727
+ if (!destAccount) {
291728
+ return wrappedCallback({
291729
+ code: grpc.status.INVALID_ARGUMENT,
291730
+ message: `Destination account "${toSelector}" is not configured`
291731
+ }, null);
291732
+ }
291733
+ try {
291734
+ if (useVerity) {
291735
+ sourceAccount.exchange.setHttpClientOverride(buildHttpClientOverrideFromMetadata(metadata, verityProverUrl, (proof, notaryPubKey) => {
291736
+ verityProof = proof;
291737
+ log.debug(`Verity proof:`, { proof, notaryPubKey });
291738
+ }), verityHttpClientOverridePredicate);
291739
+ }
291740
+ const result = await transferBinanceInternal(sourceAccount, destAccount, symbol2, transferPayload.amount);
291741
+ wrappedCallback(null, {
291742
+ proof: verityProof,
291743
+ result: JSON.stringify(result)
291744
+ });
291745
+ } catch (error48) {
291746
+ safeLogError("InternalTransfer failed", error48);
291747
+ if (error48 instanceof BrokerAccountPreconditionError) {
291748
+ return wrappedCallback({
291749
+ code: grpc.status.FAILED_PRECONDITION,
291750
+ message: getErrorMessage(error48)
291751
+ }, null);
291752
+ }
291753
+ const msg = getErrorMessage(error48);
291754
+ let code;
291755
+ if (msg.includes("Unsupported transfer direction")) {
291756
+ code = grpc.status.INVALID_ARGUMENT;
291757
+ } else if (msg.includes("unavailable in this CCXT build")) {
291758
+ code = grpc.status.UNIMPLEMENTED;
291759
+ } else {
291760
+ code = mapCcxtErrorToGrpcStatus(error48) ?? grpc.status.INTERNAL;
291761
+ }
291762
+ wrappedCallback({
291763
+ code,
291764
+ message: `InternalTransfer failed: ${msg}`
291765
+ }, null);
291766
+ }
291767
+ break;
291768
+ }
291717
291769
  default:
291718
291770
  return wrappedCallback({
291719
291771
  code: grpc.status.INVALID_ARGUMENT,
@@ -292272,4 +292324,4 @@ export {
292272
292324
  CEXBroker as default
292273
292325
  };
292274
292326
 
292275
- //# debugId=D692BFF7F06F00CD64756E2164756E21
292327
+ //# debugId=AC580D825044D41564756E2164756E21