@usherlabs/cex-broker 0.2.8 → 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.
@@ -313495,16 +313495,18 @@ if (process.env.LOG_LEVEL !== "debug") {
313495
313495
  var log = baseLogger;
313496
313496
 
313497
313497
  // src/helpers/index.ts
313498
- class WithdrawRoutingError extends Error {
313499
- }
313500
-
313501
- class WithdrawRoutingUnavailableError extends WithdrawRoutingError {
313502
- }
313503
- function isMasterBrokerAccount(account) {
313504
- return account.label === "primary" || account.role === "master";
313498
+ class BrokerAccountPreconditionError extends Error {
313499
+ constructor(message) {
313500
+ super(message);
313501
+ this.name = "BrokerAccountPreconditionError";
313502
+ }
313505
313503
  }
313506
- function isSubaccountBrokerAccount(account) {
313507
- return !isMasterBrokerAccount(account);
313504
+ function requireDestinationEmail(dest, transferType) {
313505
+ const email = dest.email?.trim();
313506
+ if (!email) {
313507
+ throw new BrokerAccountPreconditionError(`Destination account '${dest.label}' requires an email configured for ${transferType} transfers`);
313508
+ }
313509
+ return email;
313508
313510
  }
313509
313511
  function authenticateRequest(call, whitelistIps) {
313510
313512
  const clientIp = call.getPeer().split(":")[0];
@@ -313683,7 +313685,13 @@ function loadPolicy(policyPath) {
313683
313685
  const withdrawRuleEntrySchema = import_joi.default.object({
313684
313686
  exchange: import_joi.default.string().required(),
313685
313687
  network: import_joi.default.string().required(),
313686
- whitelist: import_joi.default.array().items(import_joi.default.string()).required()
313688
+ whitelist: import_joi.default.array().items(import_joi.default.string()).required(),
313689
+ coins: import_joi.default.array().items(import_joi.default.string()).optional()
313690
+ });
313691
+ const depositRuleEntrySchema = import_joi.default.object({
313692
+ exchange: import_joi.default.string().required(),
313693
+ network: import_joi.default.string().required(),
313694
+ coins: import_joi.default.array().items(import_joi.default.string()).optional()
313687
313695
  });
313688
313696
  const orderRuleSchema = import_joi.default.object({
313689
313697
  markets: import_joi.default.array().items(import_joi.default.string()).required(),
@@ -313698,7 +313706,9 @@ function loadPolicy(policyPath) {
313698
313706
  withdraw: import_joi.default.object({
313699
313707
  rule: import_joi.default.array().items(withdrawRuleEntrySchema).min(1).required()
313700
313708
  }).required(),
313701
- deposit: import_joi.default.object().pattern(import_joi.default.string(), import_joi.default.valid(null)).required(),
313709
+ deposit: import_joi.default.object({
313710
+ rule: import_joi.default.array().items(depositRuleEntrySchema).optional()
313711
+ }).required(),
313702
313712
  order: import_joi.default.object({
313703
313713
  rule: orderRuleSchema.required()
313704
313714
  }).required()
@@ -313722,9 +313732,25 @@ function normalizePolicyConfig(policy) {
313722
313732
  ...rule,
313723
313733
  exchange: rule.exchange.trim().toUpperCase(),
313724
313734
  network: rule.network.trim().toUpperCase(),
313725
- whitelist: rule.whitelist.map((address) => address.trim().toLowerCase())
313735
+ whitelist: rule.whitelist.map((address) => address.trim().toLowerCase()),
313736
+ ...rule.coins && {
313737
+ coins: rule.coins.map((c) => c.trim().toUpperCase())
313738
+ }
313726
313739
  }))
313727
313740
  },
313741
+ deposit: {
313742
+ ...policy.deposit,
313743
+ ...policy.deposit.rule && {
313744
+ rule: policy.deposit.rule.map((rule) => ({
313745
+ ...rule,
313746
+ exchange: rule.exchange.trim().toUpperCase(),
313747
+ network: rule.network.trim().toUpperCase(),
313748
+ ...rule.coins && {
313749
+ coins: rule.coins.map((c) => c.trim().toUpperCase())
313750
+ }
313751
+ }))
313752
+ }
313753
+ },
313728
313754
  order: {
313729
313755
  ...policy.order,
313730
313756
  rule: {
@@ -313751,7 +313777,24 @@ function getWithdrawRulePriority(rule, exchange, network) {
313751
313777
  }
313752
313778
  return 1;
313753
313779
  }
313754
- function validateWithdraw(policy, exchange, network, recipientAddress, _amount, _ticker) {
313780
+ function getDepositRulePriority(rule, exchange, network) {
313781
+ const exchangeMatch = rule.exchange === exchange || rule.exchange === "*";
313782
+ const networkMatch = rule.network === network || rule.network === "*";
313783
+ if (!exchangeMatch || !networkMatch) {
313784
+ return 0;
313785
+ }
313786
+ if (rule.exchange === exchange && rule.network === network) {
313787
+ return 4;
313788
+ }
313789
+ if (rule.exchange === exchange && rule.network === "*") {
313790
+ return 3;
313791
+ }
313792
+ if (rule.exchange === "*" && rule.network === network) {
313793
+ return 2;
313794
+ }
313795
+ return 1;
313796
+ }
313797
+ function validateWithdraw(policy, exchange, network, recipientAddress, _amount, ticker) {
313755
313798
  const normalizedPolicy = normalizePolicyConfig(policy);
313756
313799
  const exchangeNorm = exchange.trim().toUpperCase();
313757
313800
  const networkNorm = network.trim().toUpperCase();
@@ -313773,83 +313816,62 @@ function validateWithdraw(policy, exchange, network, recipientAddress, _amount,
313773
313816
  error: `Address ${recipientAddress} is not whitelisted for withdrawals`
313774
313817
  };
313775
313818
  }
313776
- return { valid: true };
313777
- }
313778
- function normalizeAccountSelector(selector2, metadata, defaultSelector) {
313779
- const raw = selector2?.trim().toLowerCase() ?? defaultSelector;
313780
- if (raw === "current") {
313781
- return getCurrentBrokerSelector(metadata);
313782
- }
313783
- if (raw === "primary") {
313784
- return "primary";
313785
- }
313786
- const secondaryMatch = raw.match(/^secondary:(\d+)$/);
313787
- if (secondaryMatch) {
313788
- return `secondary:${secondaryMatch[1]}`;
313819
+ const coins = withdrawRule.coins;
313820
+ if (coins && coins.length > 0 && !coins.includes("*")) {
313821
+ const tickerNorm = ticker.trim().toUpperCase();
313822
+ if (!coins.includes(tickerNorm)) {
313823
+ return {
313824
+ valid: false,
313825
+ error: `Token ${tickerNorm} is not allowed for withdrawals on ${exchangeNorm}:${networkNorm}. Allowed: [${coins.join(", ")}]`
313826
+ };
313827
+ }
313789
313828
  }
313790
- throw new WithdrawRoutingError(`Invalid account selector "${selector2}"`);
313829
+ return { valid: true };
313791
313830
  }
313792
- async function transferBinanceSubAccountToMaster(source, code, amount) {
313831
+ async function transferBinanceInternal(source, dest, code, amount) {
313793
313832
  const exchange = source.exchange;
313794
- if (typeof exchange.sapiPostSubAccountTransferSubToMaster !== "function") {
313795
- throw new WithdrawRoutingUnavailableError("Binance sub-account to master transfer is unavailable in this CCXT build");
313796
- }
313797
313833
  await source.exchange.loadMarkets();
313798
313834
  const currency = source.exchange.currency(code);
313799
- return await exchange.sapiPostSubAccountTransferSubToMaster({
313800
- asset: currency.id,
313801
- amount: source.exchange.currencyToPrecision(code, amount)
313802
- });
313803
- }
313804
- async function executeWithdrawWithRouting(args) {
313805
- const {
313806
- cex: cex3,
313807
- brokers,
313808
- metadata,
313809
- selectedBroker,
313810
- code,
313811
- amount,
313812
- recipientAddress,
313813
- network,
313814
- params,
313815
- routeViaMaster,
313816
- sourceAccount,
313817
- masterAccount
313818
- } = args;
313819
- const withdrawParams = {
313820
- ...params ?? {},
313821
- network
313822
- };
313823
- if (!routeViaMaster) {
313824
- return await selectedBroker.withdraw(code, amount, recipientAddress, undefined, withdrawParams);
313825
- }
313826
- const sourceSelector = normalizeAccountSelector(sourceAccount, metadata, "current");
313827
- const masterSelector = normalizeAccountSelector(masterAccount, metadata, "primary");
313828
- if (!brokers) {
313829
- throw new WithdrawRoutingUnavailableError("Routed withdraw requires configured broker accounts");
313830
- }
313831
- const source = resolveBrokerAccount(brokers, sourceSelector);
313832
- if (!source) {
313833
- throw new WithdrawRoutingError(`Source account ${sourceSelector} is not configured`);
313834
- }
313835
- const master = resolveBrokerAccount(brokers, masterSelector);
313836
- if (!master) {
313837
- throw new WithdrawRoutingError(`Master account ${masterSelector} is not configured`);
313838
- }
313839
- if (!isMasterBrokerAccount(master)) {
313840
- throw new WithdrawRoutingError(`Master account ${masterSelector} must resolve to the primary/master account`);
313841
- }
313842
- if (source.label === master.label) {
313843
- return await master.exchange.withdraw(code, amount, recipientAddress, undefined, withdrawParams);
313835
+ const asset = currency.id;
313836
+ const amountStr = source.exchange.currencyToPrecision(code, amount);
313837
+ const isSourceSecondary = source.label.startsWith("secondary:");
313838
+ const isDestPrimary = dest.label === "primary";
313839
+ const isDestSecondary = dest.label.startsWith("secondary:");
313840
+ const isSourcePrimary = source.label === "primary";
313841
+ if (isSourceSecondary && isDestPrimary) {
313842
+ if (typeof exchange.sapiPostSubAccountTransferSubToMaster !== "function") {
313843
+ throw new Error("Binance sub→master transfer is unavailable in this CCXT build");
313844
+ }
313845
+ return await exchange.sapiPostSubAccountTransferSubToMaster({
313846
+ asset,
313847
+ amount: amountStr
313848
+ });
313844
313849
  }
313845
- if (!isSubaccountBrokerAccount(source)) {
313846
- throw new WithdrawRoutingError(`Source account ${sourceSelector} must resolve to a subaccount when routeViaMaster is enabled`);
313850
+ if (isSourceSecondary && isDestSecondary) {
313851
+ if (typeof exchange.sapiPostSubAccountTransferSubToSub !== "function") {
313852
+ throw new Error("Binance sub→sub transfer is unavailable in this CCXT build");
313853
+ }
313854
+ const destEmail = requireDestinationEmail(dest, "sub-to-sub");
313855
+ return await exchange.sapiPostSubAccountTransferSubToSub({
313856
+ toEmail: destEmail,
313857
+ asset,
313858
+ amount: amountStr
313859
+ });
313847
313860
  }
313848
- if (cex3.trim().toLowerCase() !== "binance") {
313849
- throw new WithdrawRoutingUnavailableError(`Withdraw routing via master is not supported for ${cex3}`);
313861
+ if (isSourcePrimary && isDestSecondary) {
313862
+ if (typeof exchange.sapiPostSubAccountUniversalTransfer !== "function") {
313863
+ throw new Error("Binance universal transfer is unavailable in this CCXT build");
313864
+ }
313865
+ const destEmail = requireDestinationEmail(dest, "primary-to-sub");
313866
+ return await exchange.sapiPostSubAccountUniversalTransfer({
313867
+ fromAccountType: "SPOT",
313868
+ toAccountType: "SPOT",
313869
+ toEmail: destEmail,
313870
+ asset,
313871
+ amount: amountStr
313872
+ });
313850
313873
  }
313851
- await transferBinanceSubAccountToMaster(source, code, amount);
313852
- return await master.exchange.withdraw(code, amount, recipientAddress, undefined, withdrawParams);
313874
+ throw new Error(`Unsupported transfer direction: ${source.label} ${dest.label}`);
313853
313875
  }
313854
313876
  function isMarketPatternMatch(pattern, broker, fromToken, toToken) {
313855
313877
  const normalizedPattern = pattern.toUpperCase().trim();
@@ -313970,6 +313992,35 @@ async function resolveOrderExecution(policy, broker, cex3, fromToken, toToken, a
313970
313992
  matchedPatterns
313971
313993
  };
313972
313994
  }
313995
+ function validateDeposit(policy, exchange, network, ticker) {
313996
+ const normalizedPolicy = normalizePolicyConfig(policy);
313997
+ if (!normalizedPolicy.deposit.rule || normalizedPolicy.deposit.rule.length === 0) {
313998
+ return { valid: true };
313999
+ }
314000
+ const exchangeNorm = exchange.trim().toUpperCase();
314001
+ const networkNorm = network.trim().toUpperCase();
314002
+ const tickerNorm = ticker.trim().toUpperCase();
314003
+ const matchingRules = normalizedPolicy.deposit.rule.map((rule) => ({
314004
+ rule,
314005
+ priority: getDepositRulePriority(rule, exchangeNorm, networkNorm)
314006
+ })).filter((r) => r.priority > 0).sort((a, b2) => b2.priority - a.priority);
314007
+ const depositRule = matchingRules[0]?.rule;
314008
+ if (!depositRule) {
314009
+ return {
314010
+ valid: false,
314011
+ error: `Deposits not allowed for ${exchangeNorm}:${networkNorm}`
314012
+ };
314013
+ }
314014
+ if (depositRule.coins && depositRule.coins.length > 0 && !depositRule.coins.includes("*")) {
314015
+ if (!depositRule.coins.includes(tickerNorm)) {
314016
+ return {
314017
+ valid: false,
314018
+ error: `Token ${tickerNorm} not allowed for deposit on ${exchangeNorm}:${networkNorm}. Allowed: [${depositRule.coins.join(", ")}]`
314019
+ };
314020
+ }
314021
+ }
314022
+ return { valid: true };
314023
+ }
313973
314024
 
313974
314025
  // src/helpers/otel.ts
313975
314026
  var import_api_logs2 = __toESM(require_src7(), 1);
@@ -314303,7 +314354,8 @@ var Action = {
314303
314354
  FetchCurrency: 9,
314304
314355
  Call: 10,
314305
314356
  FetchAccountId: 11,
314306
- FetchFees: 12
314357
+ FetchFees: 12,
314358
+ InternalTransfer: 13
314307
314359
  };
314308
314360
 
314309
314361
  // src/proto/cex_broker/SubscriptionType.ts
@@ -314434,7 +314486,8 @@ var descriptor = {
314434
314486
  FetchCurrency: 9,
314435
314487
  Call: 10,
314436
314488
  FetchAccountId: 11,
314437
- FetchFees: 12
314489
+ FetchFees: 12,
314490
+ InternalTransfer: 13
314438
314491
  }
314439
314492
  }
314440
314493
  }
@@ -328020,11 +328073,13 @@ var WithdrawPayloadSchema = exports_external.object({
328020
328073
  recipientAddress: exports_external.string().min(1),
328021
328074
  amount: exports_external.coerce.number().positive(),
328022
328075
  chain: exports_external.string().min(1),
328023
- routeViaMaster: booleanLikeSchema.optional().default(false),
328024
- sourceAccount: exports_external.string().min(1).optional(),
328025
- masterAccount: exports_external.string().min(1).optional(),
328026
328076
  params: exports_external.preprocess(parseJsonString, stringNumberRecordSchema).default({})
328027
328077
  });
328078
+ var InternalTransferPayloadSchema = exports_external.object({
328079
+ amount: exports_external.coerce.number().positive(),
328080
+ fromAccount: exports_external.string().min(1).optional(),
328081
+ toAccount: exports_external.string().min(1).optional()
328082
+ });
328028
328083
  var CreateOrderPayloadSchema = exports_external.object({
328029
328084
  orderType: exports_external.enum(["market", "limit"]).default("limit"),
328030
328085
  amount: exports_external.coerce.number().positive(),
@@ -328072,6 +328127,31 @@ function safeLogError(context2, error48) {
328072
328127
  console.error(context2, error48);
328073
328128
  }
328074
328129
  }
328130
+ function mapCcxtErrorToGrpcStatus(error48) {
328131
+ if (error48 instanceof ccxt_default.AuthenticationError)
328132
+ return grpc.status.UNAUTHENTICATED;
328133
+ if (error48 instanceof ccxt_default.PermissionDenied)
328134
+ return grpc.status.PERMISSION_DENIED;
328135
+ if (error48 instanceof ccxt_default.InsufficientFunds)
328136
+ return grpc.status.FAILED_PRECONDITION;
328137
+ if (error48 instanceof ccxt_default.InvalidAddress)
328138
+ return grpc.status.INVALID_ARGUMENT;
328139
+ if (error48 instanceof ccxt_default.BadSymbol)
328140
+ return grpc.status.NOT_FOUND;
328141
+ if (error48 instanceof ccxt_default.BadRequest)
328142
+ return grpc.status.INVALID_ARGUMENT;
328143
+ if (error48 instanceof ccxt_default.NotSupported)
328144
+ return grpc.status.UNIMPLEMENTED;
328145
+ if (error48 instanceof ccxt_default.RateLimitExceeded)
328146
+ return grpc.status.RESOURCE_EXHAUSTED;
328147
+ if (error48 instanceof ccxt_default.OnMaintenance)
328148
+ return grpc.status.UNAVAILABLE;
328149
+ if (error48 instanceof ccxt_default.ExchangeNotAvailable)
328150
+ return grpc.status.UNAVAILABLE;
328151
+ if (error48 instanceof ccxt_default.NetworkError)
328152
+ return grpc.status.UNAVAILABLE;
328153
+ return;
328154
+ }
328075
328155
  function getServer(policy, brokers, whitelistIps, useVerity, verityProverUrl, otelMetrics) {
328076
328156
  const server = new grpc.Server;
328077
328157
  server.addService(cexNode.cex_service.service, {
@@ -328127,7 +328207,8 @@ function getServer(policy, brokers, whitelistIps, useVerity, verityProverUrl, ot
328127
328207
  message: "`action` AND `cex` fields are required"
328128
328208
  }, null);
328129
328209
  }
328130
- const broker = selectBroker(brokers[cex3], metadata) ?? createBroker(cex3, metadata);
328210
+ const normalizedCex = cex3.trim().toLowerCase();
328211
+ const broker = selectBroker(brokers[normalizedCex], metadata) ?? createBroker(normalizedCex, metadata);
328131
328212
  if (!broker) {
328132
328213
  return wrappedCallback({
328133
328214
  code: grpc.status.UNAUTHENTICATED,
@@ -328405,6 +328486,13 @@ function getServer(policy, brokers, whitelistIps, useVerity, verityProverUrl, ot
328405
328486
  }, null);
328406
328487
  }
328407
328488
  const fetchDepositAddresses = parsedPayload.data;
328489
+ const depositValidation = validateDeposit(policy, cex3, fetchDepositAddresses.chain, symbol2);
328490
+ if (!depositValidation.valid) {
328491
+ return wrappedCallback({
328492
+ code: grpc.status.PERMISSION_DENIED,
328493
+ message: depositValidation.error
328494
+ }, null);
328495
+ }
328408
328496
  try {
328409
328497
  const depositAddresses = broker.has.fetchDepositAddress === true ? [
328410
328498
  await broker.fetchDepositAddress(symbol2, {
@@ -328458,39 +328546,10 @@ function getServer(policy, brokers, whitelistIps, useVerity, verityProverUrl, ot
328458
328546
  }, null);
328459
328547
  }
328460
328548
  try {
328461
- let transaction;
328462
- try {
328463
- transaction = await executeWithdrawWithRouting({
328464
- cex: cex3,
328465
- brokers: brokers[cex3],
328466
- metadata,
328467
- selectedBroker: broker,
328468
- code: symbol2,
328469
- amount: transferValue.amount,
328470
- recipientAddress: transferValue.recipientAddress,
328471
- network: transferValue.chain,
328472
- params: transferValue.params,
328473
- routeViaMaster: transferValue.routeViaMaster,
328474
- sourceAccount: transferValue.sourceAccount,
328475
- masterAccount: transferValue.masterAccount
328476
- });
328477
- } catch (error48) {
328478
- if (error48 instanceof WithdrawRoutingUnavailableError) {
328479
- log.warn("Withdraw routing unavailable, falling back", {
328480
- cex: cex3,
328481
- error: error48.message
328482
- });
328483
- if (transferValue.routeViaMaster) {
328484
- throw error48;
328485
- }
328486
- transaction = await broker.withdraw(symbol2, transferValue.amount, transferValue.recipientAddress, undefined, {
328487
- ...transferValue.params ?? {},
328488
- network: transferValue.chain
328489
- });
328490
- } else {
328491
- throw error48;
328492
- }
328493
- }
328549
+ const transaction = await broker.withdraw(symbol2, transferValue.amount, transferValue.recipientAddress, undefined, {
328550
+ ...transferValue.params ?? {},
328551
+ network: transferValue.chain
328552
+ });
328494
328553
  log.info(`Withdraw Result: ${JSON.stringify(transaction)}`);
328495
328554
  wrappedCallback(null, {
328496
328555
  proof: verityProof,
@@ -328498,10 +328557,10 @@ function getServer(policy, brokers, whitelistIps, useVerity, verityProverUrl, ot
328498
328557
  });
328499
328558
  } catch (error48) {
328500
328559
  safeLogError("Withdraw failed", error48);
328501
- const message = error48 instanceof WithdrawRoutingError ? error48.message : getErrorMessage(error48);
328560
+ const code = mapCcxtErrorToGrpcStatus(error48) ?? grpc.status.INTERNAL;
328502
328561
  wrappedCallback({
328503
- code: error48 instanceof WithdrawRoutingError ? grpc.status.INVALID_ARGUMENT : grpc.status.INTERNAL,
328504
- message: `Withdraw failed: ${message}`
328562
+ code,
328563
+ message: `Withdraw failed: ${getErrorMessage(error48)}`
328505
328564
  }, null);
328506
328565
  }
328507
328566
  break;
@@ -328674,6 +328733,86 @@ function getServer(policy, brokers, whitelistIps, useVerity, verityProverUrl, ot
328674
328733
  }, null);
328675
328734
  }
328676
328735
  break;
328736
+ case Action.InternalTransfer: {
328737
+ if (!symbol2) {
328738
+ return wrappedCallback({
328739
+ code: grpc.status.INVALID_ARGUMENT,
328740
+ message: `ValidationError: Symbol required`
328741
+ }, null);
328742
+ }
328743
+ const parsedPayload = parsePayload(InternalTransferPayloadSchema, call.request.payload);
328744
+ if (!parsedPayload.success) {
328745
+ return wrappedCallback({
328746
+ code: grpc.status.INVALID_ARGUMENT,
328747
+ message: parsedPayload.message
328748
+ }, null);
328749
+ }
328750
+ const transferPayload = parsedPayload.data;
328751
+ if (normalizedCex !== "binance") {
328752
+ return wrappedCallback({
328753
+ code: grpc.status.UNIMPLEMENTED,
328754
+ message: `InternalTransfer is only supported for Binance`
328755
+ }, null);
328756
+ }
328757
+ const pool = brokers[normalizedCex];
328758
+ if (!pool) {
328759
+ return wrappedCallback({
328760
+ code: grpc.status.FAILED_PRECONDITION,
328761
+ message: `No broker accounts configured for ${normalizedCex}`
328762
+ }, null);
328763
+ }
328764
+ const fromSelector = transferPayload.fromAccount ?? getCurrentBrokerSelector(metadata);
328765
+ const toSelector = transferPayload.toAccount ?? "primary";
328766
+ const sourceAccount = resolveBrokerAccount(pool, fromSelector);
328767
+ if (!sourceAccount) {
328768
+ return wrappedCallback({
328769
+ code: grpc.status.INVALID_ARGUMENT,
328770
+ message: `Source account "${fromSelector}" is not configured`
328771
+ }, null);
328772
+ }
328773
+ const destAccount = resolveBrokerAccount(pool, toSelector);
328774
+ if (!destAccount) {
328775
+ return wrappedCallback({
328776
+ code: grpc.status.INVALID_ARGUMENT,
328777
+ message: `Destination account "${toSelector}" is not configured`
328778
+ }, null);
328779
+ }
328780
+ try {
328781
+ if (useVerity) {
328782
+ sourceAccount.exchange.setHttpClientOverride(buildHttpClientOverrideFromMetadata(metadata, verityProverUrl, (proof, notaryPubKey) => {
328783
+ verityProof = proof;
328784
+ log.debug(`Verity proof:`, { proof, notaryPubKey });
328785
+ }), verityHttpClientOverridePredicate);
328786
+ }
328787
+ const result = await transferBinanceInternal(sourceAccount, destAccount, symbol2, transferPayload.amount);
328788
+ wrappedCallback(null, {
328789
+ proof: verityProof,
328790
+ result: JSON.stringify(result)
328791
+ });
328792
+ } catch (error48) {
328793
+ safeLogError("InternalTransfer failed", error48);
328794
+ if (error48 instanceof BrokerAccountPreconditionError) {
328795
+ return wrappedCallback({
328796
+ code: grpc.status.FAILED_PRECONDITION,
328797
+ message: getErrorMessage(error48)
328798
+ }, null);
328799
+ }
328800
+ const msg = getErrorMessage(error48);
328801
+ let code;
328802
+ if (msg.includes("Unsupported transfer direction")) {
328803
+ code = grpc.status.INVALID_ARGUMENT;
328804
+ } else if (msg.includes("unavailable in this CCXT build")) {
328805
+ code = grpc.status.UNIMPLEMENTED;
328806
+ } else {
328807
+ code = mapCcxtErrorToGrpcStatus(error48) ?? grpc.status.INTERNAL;
328808
+ }
328809
+ wrappedCallback({
328810
+ code,
328811
+ message: `InternalTransfer failed: ${msg}`
328812
+ }, null);
328813
+ }
328814
+ break;
328815
+ }
328677
328816
  default:
328678
328817
  return wrappedCallback({
328679
328818
  code: grpc.status.INVALID_ARGUMENT,
@@ -14,9 +14,8 @@ export type BrokerPoolEntry = {
14
14
  primary: BrokerAccount;
15
15
  secondaryBrokers: BrokerAccount[];
16
16
  };
17
- export declare class WithdrawRoutingError extends Error {
18
- }
19
- export declare class WithdrawRoutingUnavailableError extends WithdrawRoutingError {
17
+ export declare class BrokerAccountPreconditionError extends Error {
18
+ constructor(message: string);
20
19
  }
21
20
  export declare function authenticateRequest<T, E>(call: ServerUnaryCall<T, E>, whitelistIps: string[]): boolean;
22
21
  export declare function createVerityHttpClientOverride(verityProverUrl: string, onProofCallback: (proof: string, notaryPubKey?: string) => void): (redact: string, proofTimeout: number) => HttpClientOverride;
@@ -43,24 +42,15 @@ export declare function selectBrokerAccount(brokers: BrokerPoolEntry | undefined
43
42
  */
44
43
  export declare function loadPolicy(policyPath: string): PolicyConfig;
45
44
  export declare function normalizePolicyConfig(policy: PolicyConfig): PolicyConfig;
46
- export declare function validateWithdraw(policy: PolicyConfig, exchange: string, network: string, recipientAddress: string, _amount: number, _ticker: string): {
45
+ export declare function validateWithdraw(policy: PolicyConfig, exchange: string, network: string, recipientAddress: string, _amount: number, ticker: string): {
47
46
  valid: boolean;
48
47
  error?: string;
49
48
  };
50
- export declare function executeWithdrawWithRouting(args: {
51
- cex: string;
52
- brokers: BrokerPoolEntry | undefined;
53
- metadata: Metadata;
54
- selectedBroker: Exchange;
55
- code: string;
56
- amount: number;
57
- recipientAddress: string;
58
- network: string;
59
- params?: Record<string, string | number>;
60
- routeViaMaster?: boolean;
61
- sourceAccount?: string;
62
- masterAccount?: string;
63
- }): Promise<import("@usherlabs/ccxt").Transaction>;
49
+ /**
50
+ * Routes an internal transfer to the correct Binance SAPI endpoint
51
+ * based on source and destination account types.
52
+ */
53
+ export declare function transferBinanceInternal(source: BrokerAccount, dest: BrokerAccount, code: string, amount: number): Promise<unknown>;
64
54
  /**
65
55
  * Validates order request against policy rules
66
56
  */
@@ -78,10 +68,7 @@ type OrderExecutionResolution = {
78
68
  matchedPatterns?: string[];
79
69
  };
80
70
  export declare function resolveOrderExecution(policy: PolicyConfig, broker: Exchange, cex: string, fromToken: string, toToken: string, amount: number, price: number): Promise<OrderExecutionResolution>;
81
- /**
82
- * Validates deposit request (currently empty but can be extended)
83
- */
84
- export declare function validateDeposit(_policy: PolicyConfig, _chain: string, _amount: number): {
71
+ export declare function validateDeposit(policy: PolicyConfig, exchange: string, network: string, ticker: string): {
85
72
  valid: boolean;
86
73
  error?: string;
87
74
  };