@usherlabs/cex-broker 0.2.15 → 0.2.17
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 +592 -56
- package/dist/helpers/index.d.ts +2 -1
- package/dist/helpers/travel-rule-deposit-reconciler.d.ts +152 -0
- package/dist/helpers/travel-rule.d.ts +32 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +593 -57
- package/dist/index.js.map +11 -10
- package/dist/types.d.ts +14 -0
- package/package.json +3 -2
package/dist/helpers/index.d.ts
CHANGED
|
@@ -3,7 +3,8 @@ import type { PolicyConfig } from "../types";
|
|
|
3
3
|
import { type BrokerAccount } from "./broker";
|
|
4
4
|
export { authenticateRequest } from "./auth";
|
|
5
5
|
export { applyCommonExchangeConfig, type BrokerAccount, BrokerAccountPreconditionError, type BrokerPoolEntry, createBroker, createBrokerPool, createPublicBroker, getCurrentBrokerSelector, resolveBrokerAccount, selectBroker, selectBrokerAccount, } from "./broker";
|
|
6
|
-
export { australiaQuestionnaireSchema, registerBinanceTravelRuleWithdrawEndpoint, resolveTravelRuleDecision, type TravelRuleDecision, withdrawViaLocalEntity, } from "./travel-rule";
|
|
6
|
+
export { australiaDepositQuestionnaireSchema, australiaQuestionnaireSchema, getEnabledTravelRuleDepositConfig, registerBinanceTravelRuleDepositEndpoints, registerBinanceTravelRuleWithdrawEndpoint, resolveDepositOriginatorQuestionnaire, resolveTravelRuleDecision, type TravelRuleDecision, withdrawViaLocalEntity, } from "./travel-rule";
|
|
7
|
+
export { loadTravelRuleDepositReconcilerConfigFromEnv, resolveOnChainSender, TravelRuleDepositReconciler, } from "./travel-rule-deposit-reconciler";
|
|
7
8
|
export { buildHttpClientOverrideFromMetadata, createVerityHttpClientOverride, verityHttpClientOverridePredicate, } from "./verity";
|
|
8
9
|
/**
|
|
9
10
|
* Loads and validates policy configuration
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import type { PolicyConfig, TravelRuleDepositConfig, TravelRuleDepositQuestionnaire } from "../types";
|
|
2
|
+
import type { BrokerPoolEntry } from "./broker";
|
|
3
|
+
import type { OtelMetrics } from "./otel";
|
|
4
|
+
/**
|
|
5
|
+
* Auto-clear reconciler for Binance travel-rule-frozen DEPOSITS.
|
|
6
|
+
*
|
|
7
|
+
* Under the AUSTRAC travel rule Binance credits an inbound deposit but holds it
|
|
8
|
+
* in `getUserAsset.freeze` (invisible to free+locked balances) until a per-deposit
|
|
9
|
+
* questionnaire is answered. This reconciler polls `localentity/deposit/history`
|
|
10
|
+
* for such frozen deposits and submits the questionnaire — but only for deposits
|
|
11
|
+
* whose on-chain sender is PROVEN (via `eth_getTransactionByHash`) to be one of
|
|
12
|
+
* our configured originator wallets. Everything else is left frozen and surfaced.
|
|
13
|
+
*
|
|
14
|
+
* Compliance invariant: a deposit is NEVER auto-declared unless its origin is
|
|
15
|
+
* proven ours. Undeclared origin, unresolvable sender, entity drift, or any
|
|
16
|
+
* uncertainty all fail closed (skip + surface), never "declare anyway".
|
|
17
|
+
*
|
|
18
|
+
* This runs entirely broker-internal (not through the gRPC verity path), so
|
|
19
|
+
* provide-info carries no verity proof — acceptable for a compliance attestation
|
|
20
|
+
* that is not a trading action; see the plan's edge-case notes.
|
|
21
|
+
*/
|
|
22
|
+
export type LocalEntityDeposit = {
|
|
23
|
+
tranId: string;
|
|
24
|
+
coin: string;
|
|
25
|
+
amount: string;
|
|
26
|
+
network: string;
|
|
27
|
+
txId: string;
|
|
28
|
+
travelRuleStatus: string;
|
|
29
|
+
requireQuestionnaire: boolean;
|
|
30
|
+
raw: Record<string, unknown>;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Parses one raw `localentity/deposit/history` row. Returns null when the row
|
|
34
|
+
* lacks a tranId (the only id valid for provide-info — the `capital/deposit/hisrec`
|
|
35
|
+
* id is a different id space and yields "Deposit request not found").
|
|
36
|
+
*/
|
|
37
|
+
export declare function parseLocalEntityDeposit(raw: Record<string, unknown>): LocalEntityDeposit | null;
|
|
38
|
+
/**
|
|
39
|
+
* Resolves the on-chain sender (`tx.from`) of a deposit's transaction hash via a
|
|
40
|
+
* JSON-RPC `eth_getTransactionByHash`. Returns the lowercased sender, or null
|
|
41
|
+
* when the hash is not a well-formed EVM tx hash or the tx is not found (pruned
|
|
42
|
+
* node / reorg / not yet mined) — both of which mean the origin is UNPROVEN and
|
|
43
|
+
* the caller must skip rather than assume.
|
|
44
|
+
*
|
|
45
|
+
* `tx.from` is the EOA that signed the deposit transaction. For our funding
|
|
46
|
+
* churn (owner wallet → Binance via a direct ERC20 transfer) that equals the
|
|
47
|
+
* token originator. A transfer routed through a contract could differ; hardening
|
|
48
|
+
* to the ERC20 Transfer event's `from` is possible later if that case arises.
|
|
49
|
+
*/
|
|
50
|
+
export declare function resolveOnChainSender(rpcUrl: string, txHash: string, timeoutMs?: number): Promise<string | null>;
|
|
51
|
+
/** Binance rate-limit signals (-1003 / HTTP 429 / DDoS guard). */
|
|
52
|
+
export declare function isRateLimitError(error: unknown): boolean;
|
|
53
|
+
/**
|
|
54
|
+
* A provide-info call that Binance treats as already-satisfied. Re-submitting a
|
|
55
|
+
* tranId that was already provided (a manual release, or a reconciler restart)
|
|
56
|
+
* must be idempotent, so these responses/errors count as success.
|
|
57
|
+
*/
|
|
58
|
+
export declare function isAlreadyProvidedError(error: unknown): boolean;
|
|
59
|
+
export type ReconcilerAccountState = {
|
|
60
|
+
submittedTranIds: Set<string>;
|
|
61
|
+
surfacedFailed: Set<string>;
|
|
62
|
+
backoffUntil: Map<string, number>;
|
|
63
|
+
rateLimitedUntil: number;
|
|
64
|
+
};
|
|
65
|
+
export declare function createAccountState(): ReconcilerAccountState;
|
|
66
|
+
export type ReconcileOutcome = {
|
|
67
|
+
kind: "submitted";
|
|
68
|
+
deposit: LocalEntityDeposit;
|
|
69
|
+
sender: string;
|
|
70
|
+
} | {
|
|
71
|
+
kind: "already-provided";
|
|
72
|
+
deposit: LocalEntityDeposit;
|
|
73
|
+
sender: string;
|
|
74
|
+
} | {
|
|
75
|
+
kind: "undeclared-origin";
|
|
76
|
+
deposit: LocalEntityDeposit;
|
|
77
|
+
sender: string;
|
|
78
|
+
} | {
|
|
79
|
+
kind: "unproven-origin";
|
|
80
|
+
deposit: LocalEntityDeposit;
|
|
81
|
+
} | {
|
|
82
|
+
kind: "entity-drift";
|
|
83
|
+
deposit: LocalEntityDeposit;
|
|
84
|
+
country: string | null;
|
|
85
|
+
} | {
|
|
86
|
+
kind: "failed-terminal";
|
|
87
|
+
deposit: LocalEntityDeposit;
|
|
88
|
+
} | {
|
|
89
|
+
kind: "submit-error";
|
|
90
|
+
deposit: LocalEntityDeposit;
|
|
91
|
+
error: string;
|
|
92
|
+
} | {
|
|
93
|
+
kind: "poll-error";
|
|
94
|
+
error: string;
|
|
95
|
+
};
|
|
96
|
+
export type ReconcileAccountReport = {
|
|
97
|
+
accountLabel: string;
|
|
98
|
+
frozenDeposits: LocalEntityDeposit[];
|
|
99
|
+
outcomes: ReconcileOutcome[];
|
|
100
|
+
hadActionableWork: boolean;
|
|
101
|
+
};
|
|
102
|
+
export type ReconcileAccountDeps = {
|
|
103
|
+
accountLabel: string;
|
|
104
|
+
depositConfig: TravelRuleDepositConfig;
|
|
105
|
+
expectedCountry: string;
|
|
106
|
+
failureBackoffMs: number;
|
|
107
|
+
rateLimitCooldownMs: number;
|
|
108
|
+
now: number;
|
|
109
|
+
state: ReconcilerAccountState;
|
|
110
|
+
fetchDepositHistory: () => Promise<Array<Record<string, unknown>>>;
|
|
111
|
+
fetchQuestionnaireCountry: () => Promise<string | null>;
|
|
112
|
+
resolveSender: (network: string, txId: string) => Promise<string | null>;
|
|
113
|
+
submitProvideInfo: (tranId: string, questionnaire: TravelRuleDepositQuestionnaire) => Promise<Record<string, unknown>>;
|
|
114
|
+
resolveQuestionnaire: (config: TravelRuleDepositConfig, sender: string) => TravelRuleDepositQuestionnaire | null;
|
|
115
|
+
};
|
|
116
|
+
/**
|
|
117
|
+
* Reconciles a single account once. Pure orchestration: all I/O is injected so
|
|
118
|
+
* the compliance invariants (never submit an unproven/undeclared/entity-drifted
|
|
119
|
+
* deposit) are unit-testable without a network. Fetch/poll errors are captured
|
|
120
|
+
* as outcomes rather than thrown so one bad account never stalls the loop.
|
|
121
|
+
*/
|
|
122
|
+
export declare function reconcileAccountOnce(deps: ReconcileAccountDeps): Promise<ReconcileAccountReport>;
|
|
123
|
+
export type TravelRuleDepositReconcilerConfig = {
|
|
124
|
+
rpcUrlsByNetwork: Record<string, string>;
|
|
125
|
+
pollIntervalActiveMs: number;
|
|
126
|
+
pollIntervalIdleMs: number;
|
|
127
|
+
expectedQuestionnaireCountry: string;
|
|
128
|
+
failureBackoffMs: number;
|
|
129
|
+
rateLimitCooldownMs: number;
|
|
130
|
+
};
|
|
131
|
+
/**
|
|
132
|
+
* Builds reconciler config from the environment. RPC URLs are read from
|
|
133
|
+
* `TRAVEL_RULE_RPC_URL_<NETWORK>` vars (network suffix must match Binance's
|
|
134
|
+
* network code, e.g. `TRAVEL_RULE_RPC_URL_ARBITRUM`). These are intentionally NOT
|
|
135
|
+
* `CEX_BROKER_`-prefixed so the credential env scan ignores them. Cadence and the
|
|
136
|
+
* expected questionnaire country are overridable but default to the AU rollout.
|
|
137
|
+
*/
|
|
138
|
+
export declare function loadTravelRuleDepositReconcilerConfigFromEnv(env: Record<string, string | undefined>): TravelRuleDepositReconcilerConfig;
|
|
139
|
+
export declare class TravelRuleDepositReconciler {
|
|
140
|
+
#private;
|
|
141
|
+
private readonly params;
|
|
142
|
+
constructor(params: {
|
|
143
|
+
policy: PolicyConfig;
|
|
144
|
+
brokers: Record<string, BrokerPoolEntry>;
|
|
145
|
+
config: TravelRuleDepositReconcilerConfig;
|
|
146
|
+
metrics?: OtelMetrics;
|
|
147
|
+
});
|
|
148
|
+
/** True when at least one exchange has deposit auto-clear enabled in policy. */
|
|
149
|
+
static hasEnabledExchange(policy: PolicyConfig): boolean;
|
|
150
|
+
start(): void;
|
|
151
|
+
stop(): void;
|
|
152
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Exchange } from "@usherlabs/ccxt";
|
|
2
2
|
import Joi from "joi";
|
|
3
|
-
import type { PolicyConfig, TravelRuleQuestionnaire } from "../types";
|
|
3
|
+
import type { PolicyConfig, TravelRuleDepositConfig, TravelRuleDepositQuestionnaire, TravelRuleQuestionnaire } from "../types";
|
|
4
4
|
export declare const australiaQuestionnaireSchema: Joi.ObjectSchema<any>;
|
|
5
5
|
export type TravelRuleDecision = {
|
|
6
6
|
mode: "standard";
|
|
@@ -26,6 +26,37 @@ export declare function resolveTravelRuleDecision(policy: PolicyConfig, exchange
|
|
|
26
26
|
* the generated implicit method if called again.
|
|
27
27
|
*/
|
|
28
28
|
export declare function registerBinanceTravelRuleWithdrawEndpoint(exchange: Exchange): void;
|
|
29
|
+
export declare const australiaDepositQuestionnaireSchema: Joi.ObjectSchema<any>;
|
|
30
|
+
/**
|
|
31
|
+
* Returns the enabled deposit travel-rule config for an exchange, or null when
|
|
32
|
+
* the feature is absent or disabled. Gated solely by `deposits.enabled` (not the
|
|
33
|
+
* entry's withdraw `enabled` flag): when null the reconciler does nothing for the
|
|
34
|
+
* exchange, preserving exact pre-feature behavior.
|
|
35
|
+
*/
|
|
36
|
+
export declare function getEnabledTravelRuleDepositConfig(policy: PolicyConfig, exchange: string): TravelRuleDepositConfig | null;
|
|
37
|
+
/**
|
|
38
|
+
* Looks up the questionnaire for a PROVEN on-chain sender. Matching is
|
|
39
|
+
* case-insensitive. Returns null when the sender is not a declared originator —
|
|
40
|
+
* the reconciler must then leave the deposit frozen and surface an anomaly
|
|
41
|
+
* rather than attest an origin it cannot prove is ours.
|
|
42
|
+
*/
|
|
43
|
+
export declare function resolveDepositOriginatorQuestionnaire(config: TravelRuleDepositConfig, senderAddress: string): TravelRuleDepositQuestionnaire | null;
|
|
44
|
+
/**
|
|
45
|
+
* Registers Binance's localentity deposit endpoints used by the reconciler:
|
|
46
|
+
* the two read endpoints (deposit history + questionnaire requirements) and the
|
|
47
|
+
* provide-info write. No-op for non-Binance exchanges. Idempotent.
|
|
48
|
+
*
|
|
49
|
+
* `localentity/deposit/provide-info` must be signed with the questionnaire JSON
|
|
50
|
+
* RAW (unencoded) — see the @usherlabs/ccxt patch (`binance.sign` rawencode
|
|
51
|
+
* branch). Signing it url-encoded yields Binance error -1022. The two GETs carry
|
|
52
|
+
* only simple params and sign fine either way.
|
|
53
|
+
*
|
|
54
|
+
* provide-info is a 600-weight UID-limited write (same class as
|
|
55
|
+
* capital/withdraw/apply), so it carries ccxt-binance's `4.0002` cost encoding
|
|
56
|
+
* (`.0002` = charge the UID limiter) rather than a naive `1`, which would
|
|
57
|
+
* under-throttle the write and invite -1003 once attestation is active.
|
|
58
|
+
*/
|
|
59
|
+
export declare function registerBinanceTravelRuleDepositEndpoints(exchange: Exchange): void;
|
|
29
60
|
type LocalEntityWithdrawArgs = {
|
|
30
61
|
code: string;
|
|
31
62
|
amount: number;
|