@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.
- package/dist/commands/cli.js +263 -124
- package/dist/helpers/index.d.ts +9 -22
- package/dist/index.js +264 -125
- package/dist/index.js.map +8 -8
- package/dist/proto/cex_broker/Action.d.ts +2 -1
- package/dist/proto/node.descriptor.d.ts +1 -0
- package/dist/proto/node.descriptor.ts +2 -1
- package/dist/proto/node.proto +1 -0
- package/dist/schemas/action-payloads.d.ts +6 -3
- package/dist/types.d.ts +9 -1
- package/package.json +1 -1
package/dist/commands/cli.js
CHANGED
|
@@ -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
|
|
313499
|
-
|
|
313500
|
-
|
|
313501
|
-
|
|
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
|
|
313507
|
-
|
|
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(
|
|
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
|
|
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
|
-
|
|
313777
|
-
|
|
313778
|
-
|
|
313779
|
-
|
|
313780
|
-
|
|
313781
|
-
|
|
313782
|
-
|
|
313783
|
-
|
|
313784
|
-
|
|
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
|
-
|
|
313829
|
+
return { valid: true };
|
|
313791
313830
|
}
|
|
313792
|
-
async function
|
|
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
|
-
|
|
313800
|
-
|
|
313801
|
-
|
|
313802
|
-
|
|
313803
|
-
|
|
313804
|
-
|
|
313805
|
-
|
|
313806
|
-
|
|
313807
|
-
|
|
313808
|
-
|
|
313809
|
-
|
|
313810
|
-
|
|
313811
|
-
|
|
313812
|
-
|
|
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 (
|
|
313846
|
-
|
|
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 (
|
|
313849
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
328462
|
-
|
|
328463
|
-
|
|
328464
|
-
|
|
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
|
|
328560
|
+
const code = mapCcxtErrorToGrpcStatus(error48) ?? grpc.status.INTERNAL;
|
|
328502
328561
|
wrappedCallback({
|
|
328503
|
-
code
|
|
328504
|
-
message: `Withdraw failed: ${
|
|
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,
|
package/dist/helpers/index.d.ts
CHANGED
|
@@ -14,9 +14,8 @@ export type BrokerPoolEntry = {
|
|
|
14
14
|
primary: BrokerAccount;
|
|
15
15
|
secondaryBrokers: BrokerAccount[];
|
|
16
16
|
};
|
|
17
|
-
export declare class
|
|
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,
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
};
|