perp-cli 0.11.0 → 0.12.1
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/README.md +30 -25
- package/dist/_stderr-filter.d.ts +0 -0
- package/dist/_stderr-filter.js +30 -0
- package/dist/agent-wallet/expiry.d.ts +15 -0
- package/dist/agent-wallet/expiry.js +26 -0
- package/dist/agent-wallet/lighter-keystore.d.ts +34 -0
- package/dist/agent-wallet/lighter-keystore.js +232 -0
- package/dist/agent-wallet/passphrase.d.ts +13 -0
- package/dist/agent-wallet/passphrase.js +54 -0
- package/dist/agent-wallet/signing-strategy.d.ts +67 -0
- package/dist/agent-wallet/signing-strategy.js +51 -0
- package/dist/agent-wallet/store.d.ts +37 -0
- package/dist/agent-wallet/store.js +172 -0
- package/dist/api/public/_http.d.ts +15 -0
- package/dist/api/public/_http.js +24 -0
- package/dist/api/public/hyperliquid.js +36 -24
- package/dist/api/public/lighter.d.ts +7 -1
- package/dist/api/public/lighter.js +85 -37
- package/dist/api/public/pacifica.js +47 -18
- package/dist/bridge-engine.js +102 -49
- package/dist/cli-spec.js +1 -2
- package/dist/commands/account.js +2 -500
- package/dist/commands/agent.d.ts +8 -2
- package/dist/commands/agent.js +1903 -366
- package/dist/commands/alerts.js +3 -1
- package/dist/commands/arb/index.js +37 -7
- package/dist/commands/arb-auto.js +44 -11
- package/dist/commands/arb.js +83 -34
- package/dist/commands/bot.d.ts +1 -1
- package/dist/commands/bot.js +18 -14
- package/dist/commands/bridge.d.ts +1 -1
- package/dist/commands/bridge.js +4 -4
- package/dist/commands/funds.d.ts +1 -1
- package/dist/commands/funds.js +18 -68
- package/dist/commands/jobs.d.ts +1 -1
- package/dist/commands/jobs.js +2 -2
- package/dist/commands/manage.d.ts +1 -1
- package/dist/commands/manage.js +12 -84
- package/dist/commands/plan.d.ts +1 -1
- package/dist/commands/plan.js +2 -2
- package/dist/commands/portfolio.d.ts +19 -3
- package/dist/commands/portfolio.js +747 -163
- package/dist/commands/rebalance.d.ts +1 -1
- package/dist/commands/rebalance.js +18 -12
- package/dist/commands/risk.js +2 -6
- package/dist/commands/settings.js +30 -53
- package/dist/commands/trade.js +16 -15
- package/dist/commands/wallet.d.ts +2 -1
- package/dist/commands/wallet.js +756 -28
- package/dist/errors.d.ts +64 -0
- package/dist/errors.js +82 -2
- package/dist/event-stream.js +12 -1
- package/dist/exchanges/aster-typed-data.d.ts +156 -0
- package/dist/exchanges/aster-typed-data.js +189 -0
- package/dist/exchanges/aster.d.ts +60 -11
- package/dist/exchanges/aster.js +278 -136
- package/dist/exchanges/hyperliquid.d.ts +14 -0
- package/dist/exchanges/hyperliquid.js +84 -34
- package/dist/exchanges/lighter-spot.js +14 -3
- package/dist/exchanges/lighter.d.ts +66 -0
- package/dist/exchanges/lighter.js +199 -27
- package/dist/exchanges/pacifica-typed-data.d.ts +75 -0
- package/dist/exchanges/pacifica-typed-data.js +74 -0
- package/dist/exchanges/pacifica.d.ts +29 -1
- package/dist/exchanges/pacifica.js +94 -6
- package/dist/funding/rates.js +59 -21
- package/dist/guardrail/perp-guardrail.js +9 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +268 -360
- package/dist/jobs.js +3 -1
- package/dist/mcp-server.js +85 -83
- package/dist/position-history.js +47 -4
- package/dist/settings.d.ts +67 -3
- package/dist/settings.js +1 -2
- package/dist/signer/ows-evm.d.ts +14 -0
- package/dist/signer/ows-evm.js +28 -9
- package/dist/smart-order.d.ts +8 -2
- package/dist/smart-order.js +46 -11
- package/dist/strategies/funding-arb.js +29 -8
- package/dist/trade-validator.js +30 -5
- package/dist/utils.d.ts +10 -0
- package/dist/utils.js +19 -0
- package/package.json +2 -2
- package/skills/perp-cli/SKILL.md +23 -14
- package/skills/perp-cli/references/agent-operations.md +6 -4
- package/skills/perp-cli/references/commands.md +55 -34
- package/skills/perp-cli/scripts/preflight.sh +4 -4
- package/skills/perp-cli/scripts/spot-perp-scan.sh +2 -2
- package/dist/__tests__/alert-logic.test.d.ts +0 -1
- package/dist/__tests__/alert-logic.test.js +0 -107
- package/dist/__tests__/arb-auto-3dex.test.d.ts +0 -1
- package/dist/__tests__/arb-auto-3dex.test.js +0 -397
- package/dist/__tests__/arb-history-stats.test.d.ts +0 -1
- package/dist/__tests__/arb-history-stats.test.js +0 -176
- package/dist/__tests__/arb-logic.test.d.ts +0 -1
- package/dist/__tests__/arb-logic.test.js +0 -84
- package/dist/__tests__/arb-manage.test.d.ts +0 -1
- package/dist/__tests__/arb-manage.test.js +0 -253
- package/dist/__tests__/arb-new-features.test.d.ts +0 -1
- package/dist/__tests__/arb-new-features.test.js +0 -457
- package/dist/__tests__/arb-sizing.test.d.ts +0 -1
- package/dist/__tests__/arb-sizing.test.js +0 -48
- package/dist/__tests__/arb-state.test.d.ts +0 -1
- package/dist/__tests__/arb-state.test.js +0 -284
- package/dist/__tests__/arb-userflow.test.d.ts +0 -1
- package/dist/__tests__/arb-userflow.test.js +0 -945
- package/dist/__tests__/arb-utils.test.d.ts +0 -1
- package/dist/__tests__/arb-utils.test.js +0 -264
- package/dist/__tests__/bot-conditions.test.d.ts +0 -1
- package/dist/__tests__/bot-conditions.test.js +0 -341
- package/dist/__tests__/client-id-tracker.test.d.ts +0 -1
- package/dist/__tests__/client-id-tracker.test.js +0 -137
- package/dist/__tests__/commands/new-atomic-commands.test.d.ts +0 -1
- package/dist/__tests__/commands/new-atomic-commands.test.js +0 -502
- package/dist/__tests__/commands/order-intent.test.d.ts +0 -1
- package/dist/__tests__/commands/order-intent.test.js +0 -600
- package/dist/__tests__/commands/trade-commands.test.d.ts +0 -1
- package/dist/__tests__/commands/trade-commands.test.js +0 -821
- package/dist/__tests__/config.test.d.ts +0 -1
- package/dist/__tests__/config.test.js +0 -86
- package/dist/__tests__/cross-chain-margin.test.d.ts +0 -1
- package/dist/__tests__/cross-chain-margin.test.js +0 -287
- package/dist/__tests__/dex-asset-map.test.d.ts +0 -1
- package/dist/__tests__/dex-asset-map.test.js +0 -191
- package/dist/__tests__/errors.test.d.ts +0 -1
- package/dist/__tests__/errors.test.js +0 -110
- package/dist/__tests__/event-stream.test.d.ts +0 -1
- package/dist/__tests__/event-stream.test.js +0 -276
- package/dist/__tests__/exchanges/interface.test.d.ts +0 -1
- package/dist/__tests__/exchanges/interface.test.js +0 -132
- package/dist/__tests__/exchanges/mock-adapter.d.ts +0 -69
- package/dist/__tests__/exchanges/mock-adapter.js +0 -137
- package/dist/__tests__/execution-log.test.d.ts +0 -1
- package/dist/__tests__/execution-log.test.js +0 -106
- package/dist/__tests__/funding-calc.test.d.ts +0 -1
- package/dist/__tests__/funding-calc.test.js +0 -71
- package/dist/__tests__/funding-history.test.d.ts +0 -1
- package/dist/__tests__/funding-history.test.js +0 -343
- package/dist/__tests__/funding-rates.test.d.ts +0 -1
- package/dist/__tests__/funding-rates.test.js +0 -342
- package/dist/__tests__/funding.test.d.ts +0 -1
- package/dist/__tests__/funding.test.js +0 -173
- package/dist/__tests__/gap-logic.test.d.ts +0 -1
- package/dist/__tests__/gap-logic.test.js +0 -43
- package/dist/__tests__/hip3-dex.test.d.ts +0 -1
- package/dist/__tests__/hip3-dex.test.js +0 -234
- package/dist/__tests__/integration/agent-features.integration.test.d.ts +0 -1
- package/dist/__tests__/integration/agent-features.integration.test.js +0 -553
- package/dist/__tests__/integration/atomic-commands.integration.test.d.ts +0 -13
- package/dist/__tests__/integration/atomic-commands.integration.test.js +0 -246
- package/dist/__tests__/integration/bridge-simulation.integration.test.d.ts +0 -1
- package/dist/__tests__/integration/bridge-simulation.integration.test.js +0 -453
- package/dist/__tests__/integration/bridge-strict.integration.test.d.ts +0 -1
- package/dist/__tests__/integration/bridge-strict.integration.test.js +0 -812
- package/dist/__tests__/integration/bridge.integration.test.d.ts +0 -1
- package/dist/__tests__/integration/bridge.integration.test.js +0 -309
- package/dist/__tests__/integration/cli-e2e.integration.test.d.ts +0 -1
- package/dist/__tests__/integration/cli-e2e.integration.test.js +0 -202
- package/dist/__tests__/integration/dex-arb.integration.test.d.ts +0 -1
- package/dist/__tests__/integration/dex-arb.integration.test.js +0 -116
- package/dist/__tests__/integration/envelope-consistency.integration.test.d.ts +0 -13
- package/dist/__tests__/integration/envelope-consistency.integration.test.js +0 -205
- package/dist/__tests__/integration/hip3-dex.integration.test.d.ts +0 -1
- package/dist/__tests__/integration/hip3-dex.integration.test.js +0 -147
- package/dist/__tests__/integration/hyperliquid.integration.test.d.ts +0 -1
- package/dist/__tests__/integration/hyperliquid.integration.test.js +0 -79
- package/dist/__tests__/integration/lighter.integration.test.d.ts +0 -1
- package/dist/__tests__/integration/lighter.integration.test.js +0 -53
- package/dist/__tests__/integration/new-commands-e2e.integration.test.d.ts +0 -9
- package/dist/__tests__/integration/new-commands-e2e.integration.test.js +0 -236
- package/dist/__tests__/integration/order-verification.integration.test.d.ts +0 -1
- package/dist/__tests__/integration/order-verification.integration.test.js +0 -321
- package/dist/__tests__/integration/pacifica.integration.test.d.ts +0 -1
- package/dist/__tests__/integration/pacifica.integration.test.js +0 -75
- package/dist/__tests__/integration/response-shapes.integration.test.d.ts +0 -1
- package/dist/__tests__/integration/response-shapes.integration.test.js +0 -278
- package/dist/__tests__/liquidity.test.d.ts +0 -1
- package/dist/__tests__/liquidity.test.js +0 -225
- package/dist/__tests__/plan-executor.test.d.ts +0 -1
- package/dist/__tests__/plan-executor.test.js +0 -314
- package/dist/__tests__/position-history.test.d.ts +0 -1
- package/dist/__tests__/position-history.test.js +0 -367
- package/dist/__tests__/retry.test.d.ts +0 -1
- package/dist/__tests__/retry.test.js +0 -310
- package/dist/__tests__/risk-assessment.test.d.ts +0 -1
- package/dist/__tests__/risk-assessment.test.js +0 -288
- package/dist/__tests__/security-adversarial.test.d.ts +0 -1
- package/dist/__tests__/security-adversarial.test.js +0 -574
- package/dist/__tests__/strategies.test.d.ts +0 -1
- package/dist/__tests__/strategies.test.js +0 -539
- package/dist/__tests__/trade-execution.test.d.ts +0 -1
- package/dist/__tests__/trade-execution.test.js +0 -129
- package/dist/__tests__/trade-validator.test.d.ts +0 -1
- package/dist/__tests__/trade-validator.test.js +0 -655
- package/dist/__tests__/utils.test.d.ts +0 -1
- package/dist/__tests__/utils.test.js +0 -76
- package/dist/arb/reconcile.d.ts +0 -28
- package/dist/arb/reconcile.js +0 -162
- package/dist/commands/alert.d.ts +0 -2
- package/dist/commands/alert.js +0 -421
- package/dist/commands/analytics.d.ts +0 -3
- package/dist/commands/analytics.js +0 -311
- package/dist/commands/arb-manage.d.ts +0 -5
- package/dist/commands/arb-manage.js +0 -5
- package/dist/commands/dashboard.d.ts +0 -3
- package/dist/commands/dashboard.js +0 -176
- package/dist/commands/deposit.d.ts +0 -4
- package/dist/commands/deposit.js +0 -573
- package/dist/commands/dex.d.ts +0 -3
- package/dist/commands/dex.js +0 -120
- package/dist/commands/env.d.ts +0 -2
- package/dist/commands/env.js +0 -136
- package/dist/commands/funding.d.ts +0 -3
- package/dist/commands/funding.js +0 -554
- package/dist/commands/gap.d.ts +0 -2
- package/dist/commands/gap.js +0 -305
- package/dist/commands/health.d.ts +0 -2
- package/dist/commands/health.js +0 -67
- package/dist/commands/multileg.d.ts +0 -16
- package/dist/commands/multileg.js +0 -174
- package/dist/commands/ows.d.ts +0 -2
- package/dist/commands/ows.js +0 -887
- package/dist/commands/pnl.d.ts +0 -12
- package/dist/commands/pnl.js +0 -243
- package/dist/commands/run.d.ts +0 -3
- package/dist/commands/run.js +0 -202
- package/dist/commands/stream.d.ts +0 -5
- package/dist/commands/stream.js +0 -200
- package/dist/commands/withdraw.d.ts +0 -3
- package/dist/commands/withdraw.js +0 -187
- package/dist/exchanges/edgex.d.ts +0 -62
- package/dist/exchanges/edgex.js +0 -552
- package/dist/exchanges/nado.d.ts +0 -52
- package/dist/exchanges/nado.js +0 -223
- package/dist/funding/index.d.ts +0 -3
- package/dist/funding/index.js +0 -3
- package/dist/retry.d.ts +0 -74
- package/dist/retry.js +0 -129
- package/dist/ws/market-feed.d.ts +0 -74
- package/dist/ws/market-feed.js +0 -227
package/dist/errors.d.ts
CHANGED
|
@@ -50,6 +50,62 @@ export declare const ERROR_CODES: {
|
|
|
50
50
|
readonly status: 409;
|
|
51
51
|
readonly retryable: false;
|
|
52
52
|
};
|
|
53
|
+
readonly NO_SIGNER_AVAILABLE: {
|
|
54
|
+
readonly code: "NO_SIGNER_AVAILABLE";
|
|
55
|
+
readonly status: 401;
|
|
56
|
+
readonly retryable: false;
|
|
57
|
+
};
|
|
58
|
+
readonly AGENT_NOT_REGISTERED: {
|
|
59
|
+
readonly code: "AGENT_NOT_REGISTERED";
|
|
60
|
+
readonly status: 401;
|
|
61
|
+
readonly retryable: false;
|
|
62
|
+
};
|
|
63
|
+
readonly AGENT_EXPIRED: {
|
|
64
|
+
readonly code: "AGENT_EXPIRED";
|
|
65
|
+
readonly status: 401;
|
|
66
|
+
readonly retryable: false;
|
|
67
|
+
};
|
|
68
|
+
readonly POLICY_DENIED: {
|
|
69
|
+
readonly code: "POLICY_DENIED";
|
|
70
|
+
readonly status: 403;
|
|
71
|
+
readonly retryable: false;
|
|
72
|
+
};
|
|
73
|
+
readonly KEY_NOT_FOUND: {
|
|
74
|
+
readonly code: "KEY_NOT_FOUND";
|
|
75
|
+
readonly status: 404;
|
|
76
|
+
readonly retryable: false;
|
|
77
|
+
};
|
|
78
|
+
readonly WALLET_LOCKED: {
|
|
79
|
+
readonly code: "WALLET_LOCKED";
|
|
80
|
+
readonly status: 423;
|
|
81
|
+
readonly retryable: false;
|
|
82
|
+
};
|
|
83
|
+
readonly APPROVE_PARTIAL: {
|
|
84
|
+
readonly code: "APPROVE_PARTIAL";
|
|
85
|
+
readonly status: 500;
|
|
86
|
+
readonly retryable: false;
|
|
87
|
+
};
|
|
88
|
+
readonly APPROVE_FAILED: {
|
|
89
|
+
readonly code: "APPROVE_FAILED";
|
|
90
|
+
readonly status: 500;
|
|
91
|
+
readonly retryable: false;
|
|
92
|
+
};
|
|
93
|
+
readonly LOCK_HELD: {
|
|
94
|
+
readonly code: "LOCK_HELD";
|
|
95
|
+
readonly status: 423;
|
|
96
|
+
readonly retryable: true;
|
|
97
|
+
readonly retryAfterMs: 5000;
|
|
98
|
+
};
|
|
99
|
+
readonly PASSPHRASE_REQUIRED: {
|
|
100
|
+
readonly code: "PASSPHRASE_REQUIRED";
|
|
101
|
+
readonly status: 401;
|
|
102
|
+
readonly retryable: false;
|
|
103
|
+
};
|
|
104
|
+
readonly NOT_IMPLEMENTED: {
|
|
105
|
+
readonly code: "NOT_IMPLEMENTED";
|
|
106
|
+
readonly status: 501;
|
|
107
|
+
readonly retryable: false;
|
|
108
|
+
};
|
|
53
109
|
readonly EXCHANGE_UNREACHABLE: {
|
|
54
110
|
readonly code: "EXCHANGE_UNREACHABLE";
|
|
55
111
|
readonly status: 503;
|
|
@@ -96,7 +152,15 @@ export interface StructuredError {
|
|
|
96
152
|
retryAfterMs?: number;
|
|
97
153
|
exchange?: string;
|
|
98
154
|
details?: Record<string, unknown>;
|
|
155
|
+
/** Actionable hint for automated callers (AC-19) */
|
|
156
|
+
remediation?: string;
|
|
99
157
|
}
|
|
158
|
+
/**
|
|
159
|
+
* Extract a human-readable message from any thrown value.
|
|
160
|
+
* Handles Error instances, plain objects with a `.message` field (string OR object),
|
|
161
|
+
* and circular-safe stringification fallback.
|
|
162
|
+
*/
|
|
163
|
+
export declare function extractErrorMessage(err: unknown): string;
|
|
100
164
|
/**
|
|
101
165
|
* Classify an error from any exchange into a structured error code.
|
|
102
166
|
* Pattern-matches on error messages to detect known error types.
|
package/dist/errors.js
CHANGED
|
@@ -11,6 +11,18 @@ export const ERROR_CODES = {
|
|
|
11
11
|
SIZE_TOO_LARGE: { code: "SIZE_TOO_LARGE", status: 400, retryable: false },
|
|
12
12
|
RISK_VIOLATION: { code: "RISK_VIOLATION", status: 403, retryable: false },
|
|
13
13
|
DUPLICATE_ORDER: { code: "DUPLICATE_ORDER", status: 409, retryable: false },
|
|
14
|
+
// Agent-wallet error codes (OWS / Phase 2a)
|
|
15
|
+
NO_SIGNER_AVAILABLE: { code: "NO_SIGNER_AVAILABLE", status: 401, retryable: false },
|
|
16
|
+
AGENT_NOT_REGISTERED: { code: "AGENT_NOT_REGISTERED", status: 401, retryable: false },
|
|
17
|
+
AGENT_EXPIRED: { code: "AGENT_EXPIRED", status: 401, retryable: false },
|
|
18
|
+
POLICY_DENIED: { code: "POLICY_DENIED", status: 403, retryable: false },
|
|
19
|
+
KEY_NOT_FOUND: { code: "KEY_NOT_FOUND", status: 404, retryable: false },
|
|
20
|
+
WALLET_LOCKED: { code: "WALLET_LOCKED", status: 423, retryable: false },
|
|
21
|
+
APPROVE_PARTIAL: { code: "APPROVE_PARTIAL", status: 500, retryable: false },
|
|
22
|
+
APPROVE_FAILED: { code: "APPROVE_FAILED", status: 500, retryable: false },
|
|
23
|
+
LOCK_HELD: { code: "LOCK_HELD", status: 423, retryable: true, retryAfterMs: 5000 },
|
|
24
|
+
PASSPHRASE_REQUIRED: { code: "PASSPHRASE_REQUIRED", status: 401, retryable: false },
|
|
25
|
+
NOT_IMPLEMENTED: { code: "NOT_IMPLEMENTED", status: 501, retryable: false },
|
|
14
26
|
// 5xx - System / transient errors
|
|
15
27
|
EXCHANGE_UNREACHABLE: { code: "EXCHANGE_UNREACHABLE", status: 503, retryable: true },
|
|
16
28
|
RATE_LIMITED: { code: "RATE_LIMITED", status: 429, retryable: true, retryAfterMs: 1000 },
|
|
@@ -20,12 +32,37 @@ export const ERROR_CODES = {
|
|
|
20
32
|
TIMEOUT: { code: "TIMEOUT", status: 504, retryable: true },
|
|
21
33
|
UNKNOWN: { code: "UNKNOWN", status: 500, retryable: false },
|
|
22
34
|
};
|
|
35
|
+
/**
|
|
36
|
+
* Extract a human-readable message from any thrown value.
|
|
37
|
+
* Handles Error instances, plain objects with a `.message` field (string OR object),
|
|
38
|
+
* and circular-safe stringification fallback.
|
|
39
|
+
*/
|
|
40
|
+
export function extractErrorMessage(err) {
|
|
41
|
+
if (err instanceof Error)
|
|
42
|
+
return err.message;
|
|
43
|
+
if (err && typeof err === "object") {
|
|
44
|
+
const m = err.message;
|
|
45
|
+
if (typeof m === "string")
|
|
46
|
+
return m;
|
|
47
|
+
if (m !== undefined) {
|
|
48
|
+
try {
|
|
49
|
+
return JSON.stringify(m);
|
|
50
|
+
}
|
|
51
|
+
catch { /* circular */ }
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
return JSON.stringify(err);
|
|
55
|
+
}
|
|
56
|
+
catch { /* circular */ }
|
|
57
|
+
}
|
|
58
|
+
return String(err);
|
|
59
|
+
}
|
|
23
60
|
/**
|
|
24
61
|
* Classify an error from any exchange into a structured error code.
|
|
25
62
|
* Pattern-matches on error messages to detect known error types.
|
|
26
63
|
*/
|
|
27
64
|
export function classifyError(err, exchange) {
|
|
28
|
-
const message =
|
|
65
|
+
const message = extractErrorMessage(err);
|
|
29
66
|
const lower = message.toLowerCase();
|
|
30
67
|
// Margin-specific checks first (before generic "insufficient" catch)
|
|
31
68
|
if (lower.includes("margin") && (lower.includes("insufficient") || lower.includes("not enough"))) {
|
|
@@ -43,6 +80,39 @@ export function classifyError(err, exchange) {
|
|
|
43
80
|
if (lower.includes("timeout") || lower.includes("timed out") || lower.includes("etimedout")) {
|
|
44
81
|
return { ...ERROR_CODES.TIMEOUT, message, exchange };
|
|
45
82
|
}
|
|
83
|
+
// OWS / agent-wallet specific matchers — MUST precede generic "sign" / "not found" blocks
|
|
84
|
+
// so "wallet is locked, cannot sign" → WALLET_LOCKED (not SIGNATURE_FAILED)
|
|
85
|
+
// and "key not found" → KEY_NOT_FOUND (not SYMBOL_NOT_FOUND / default)
|
|
86
|
+
if (lower.includes("policy denied") || (lower.includes("policy") && lower.includes("denied"))) {
|
|
87
|
+
return { ...ERROR_CODES.POLICY_DENIED, message, exchange };
|
|
88
|
+
}
|
|
89
|
+
if (lower.includes("wallet is locked") || lower.includes("wallet locked")) {
|
|
90
|
+
return { ...ERROR_CODES.WALLET_LOCKED, message, exchange };
|
|
91
|
+
}
|
|
92
|
+
if (lower.includes("key not found") || lower.includes("api key not found")) {
|
|
93
|
+
return { ...ERROR_CODES.KEY_NOT_FOUND, message, exchange };
|
|
94
|
+
}
|
|
95
|
+
if (lower.includes("no signer") || lower.includes("signer unavailable") || lower.includes("no auth")) {
|
|
96
|
+
return { ...ERROR_CODES.NO_SIGNER_AVAILABLE, message, exchange };
|
|
97
|
+
}
|
|
98
|
+
if (lower.includes("agent not registered")) {
|
|
99
|
+
return { ...ERROR_CODES.AGENT_NOT_REGISTERED, message, exchange };
|
|
100
|
+
}
|
|
101
|
+
if (lower.includes("agent expired") || lower.includes("agent has expired")) {
|
|
102
|
+
return { ...ERROR_CODES.AGENT_EXPIRED, message, exchange };
|
|
103
|
+
}
|
|
104
|
+
if (lower.includes("lock held") || lower.includes("lockfile held")) {
|
|
105
|
+
return { ...ERROR_CODES.LOCK_HELD, message, exchange };
|
|
106
|
+
}
|
|
107
|
+
if (lower.includes("passphrase required") || lower.includes("passphrase missing")) {
|
|
108
|
+
return { ...ERROR_CODES.PASSPHRASE_REQUIRED, message, exchange };
|
|
109
|
+
}
|
|
110
|
+
if (lower.includes("partial") && (lower.includes("approve") || lower.includes("approval"))) {
|
|
111
|
+
return { ...ERROR_CODES.APPROVE_PARTIAL, message, exchange };
|
|
112
|
+
}
|
|
113
|
+
if (lower.includes("approve_failed") || (lower.includes("approve failed") && lower.includes("clean"))) {
|
|
114
|
+
return { ...ERROR_CODES.APPROVE_FAILED, message, exchange };
|
|
115
|
+
}
|
|
46
116
|
if (lower.includes("not found") && (lower.includes("symbol") || lower.includes("market") || lower.includes("asset"))) {
|
|
47
117
|
return { ...ERROR_CODES.SYMBOL_NOT_FOUND, message, exchange };
|
|
48
118
|
}
|
|
@@ -79,6 +149,16 @@ export class PerpError extends Error {
|
|
|
79
149
|
constructor(code, message, details) {
|
|
80
150
|
super(message);
|
|
81
151
|
this.name = "PerpError";
|
|
82
|
-
|
|
152
|
+
// Lift `remediation` from details to top-level StructuredError field (AC-19)
|
|
153
|
+
const remediation = details?.remediation;
|
|
154
|
+
const strippedDetails = details ? { ...details } : undefined;
|
|
155
|
+
if (strippedDetails)
|
|
156
|
+
delete strippedDetails.remediation;
|
|
157
|
+
this.structured = {
|
|
158
|
+
...ERROR_CODES[code],
|
|
159
|
+
message,
|
|
160
|
+
...(remediation !== undefined ? { remediation } : {}),
|
|
161
|
+
...(strippedDetails && Object.keys(strippedDetails).length > 0 ? { details: strippedDetails } : {}),
|
|
162
|
+
};
|
|
83
163
|
}
|
|
84
164
|
}
|
package/dist/event-stream.js
CHANGED
|
@@ -48,9 +48,20 @@ export async function startEventStream(adapter, opts) {
|
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
// Closed positions
|
|
51
|
+
// SSOT rule #2: emit realizedPnl explicitly so position-history can
|
|
52
|
+
// require the field. We can't reach back into the exchange to fetch the
|
|
53
|
+
// true realized P&L from a polling stream — but the last unrealized
|
|
54
|
+
// snapshot before the position disappears is the closest proxy and
|
|
55
|
+
// becomes realized at close. Surfacing it under realizedPnl makes the
|
|
56
|
+
// contract explicit.
|
|
51
57
|
for (const [sym, prev] of prevPositions) {
|
|
52
58
|
if (!currentPositions.has(sym)) {
|
|
53
|
-
emit({
|
|
59
|
+
emit({
|
|
60
|
+
type: "position_closed",
|
|
61
|
+
exchange: adapter.name,
|
|
62
|
+
timestamp: ts,
|
|
63
|
+
data: { ...prev, realizedPnl: prev.unrealizedPnl },
|
|
64
|
+
});
|
|
54
65
|
}
|
|
55
66
|
}
|
|
56
67
|
// ── Liquidation warnings ──
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Aster V3 EIP-712 typed-data builders.
|
|
3
|
+
*
|
|
4
|
+
* Pure functions — no side effects, no fs/fetch/env reads.
|
|
5
|
+
* All field ordering and serialization matches the HypurrQuant_FE mainnet
|
|
6
|
+
* production reference: AsterPerpAdapter.ts:456-700.
|
|
7
|
+
*
|
|
8
|
+
* Key conventions:
|
|
9
|
+
* - Domain A (chainId=56) — master-signed ops: approveAgent, delAgent, approveBuilder
|
|
10
|
+
* - Domain B (chainId=1666) — agent-signed per-order ops
|
|
11
|
+
* - uint256 fields serialized as decimal strings (never BigInt) to survive JSON.stringify
|
|
12
|
+
* - EIP-712 message keys are PascalCase; HTTP query-string keys are camelCase
|
|
13
|
+
*/
|
|
14
|
+
/** Domain A: used for master-signed ops (ApproveAgent, DelAgent, ApproveBuilder). */
|
|
15
|
+
export declare const ASTER_DOMAIN_A: {
|
|
16
|
+
readonly name: "AsterSignTransaction";
|
|
17
|
+
readonly version: "1";
|
|
18
|
+
readonly chainId: 56;
|
|
19
|
+
readonly verifyingContract: "0x0000000000000000000000000000000000000000";
|
|
20
|
+
};
|
|
21
|
+
/** Domain B: used for agent-signed per-request ops (order, cancel, leverage). */
|
|
22
|
+
export declare const ASTER_DOMAIN_B: {
|
|
23
|
+
readonly chainId: 1666;
|
|
24
|
+
readonly name: "AsterSignTransaction";
|
|
25
|
+
readonly version: "1";
|
|
26
|
+
readonly verifyingContract: "0x0000000000000000000000000000000000000000";
|
|
27
|
+
};
|
|
28
|
+
/** EIP-712 types for Domain B order signing — single msg string (URL-encoded query string). */
|
|
29
|
+
export declare const ORDER_TYPES: {
|
|
30
|
+
readonly Message: readonly [{
|
|
31
|
+
readonly name: "msg";
|
|
32
|
+
readonly type: "string";
|
|
33
|
+
}];
|
|
34
|
+
};
|
|
35
|
+
type Eip712Field = {
|
|
36
|
+
name: string;
|
|
37
|
+
type: string;
|
|
38
|
+
};
|
|
39
|
+
type AsterChain = "Mainnet" | "Testnet";
|
|
40
|
+
export interface ApproveAgentParams {
|
|
41
|
+
user: `0x${string}`;
|
|
42
|
+
agentAddress: `0x${string}`;
|
|
43
|
+
agentName: string;
|
|
44
|
+
/** Unix milliseconds timestamp when the agent expires. Serialized as decimal string. */
|
|
45
|
+
expiredMs: number | string;
|
|
46
|
+
canPerpTrade: boolean;
|
|
47
|
+
canSpotTrade: boolean;
|
|
48
|
+
canWithdraw: boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Optional IP whitelist. Include (even as "") when defined and non-null.
|
|
51
|
+
* Undefined/null = field omitted from EIP-712 struct.
|
|
52
|
+
*/
|
|
53
|
+
ipWhitelist?: string;
|
|
54
|
+
/** Optional builder address. When present, Builder + MaxFeeRate are included. */
|
|
55
|
+
builder?: string;
|
|
56
|
+
/** Required when builder is set. */
|
|
57
|
+
maxFeeRate?: string;
|
|
58
|
+
/** Optional builder name. Independently gated by its own undefined-check. */
|
|
59
|
+
builderName?: string;
|
|
60
|
+
/** Microsecond-precision nonce (Date.now() * 1000 + counter). Serialized as decimal string. */
|
|
61
|
+
nonceMicros: number | string;
|
|
62
|
+
asterChain?: AsterChain;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Build the EIP-712 typed-data payload for `POST /fapi/v3/approveAgent`.
|
|
66
|
+
*
|
|
67
|
+
* Field order in types array and message object matches HypurrQuant_FE
|
|
68
|
+
* AsterPerpAdapter.ts:481-533 exactly:
|
|
69
|
+
* AgentName, AgentAddress, [IpWhitelist?,] Expired, CanSpotTrade, CanPerpTrade,
|
|
70
|
+
* CanWithdraw, [Builder?, MaxFeeRate?, BuilderName?,] AsterChain, User, Nonce
|
|
71
|
+
*/
|
|
72
|
+
export declare function buildApproveAgentTypedData(params: ApproveAgentParams): {
|
|
73
|
+
domain: typeof ASTER_DOMAIN_A;
|
|
74
|
+
types: {
|
|
75
|
+
ApproveAgent: Eip712Field[];
|
|
76
|
+
};
|
|
77
|
+
primaryType: "ApproveAgent";
|
|
78
|
+
message: Record<string, unknown>;
|
|
79
|
+
};
|
|
80
|
+
export interface DelAgentParams {
|
|
81
|
+
user: `0x${string}`;
|
|
82
|
+
agentAddress: `0x${string}`;
|
|
83
|
+
nonceMicros: number | string;
|
|
84
|
+
asterChain?: AsterChain;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Build the EIP-712 typed-data payload for `DELETE /fapi/v3/agent`.
|
|
88
|
+
*
|
|
89
|
+
* TODO: confirm DelAgent struct vs Aster docs at Step 0b spike.
|
|
90
|
+
* HypurrQuant_FE does not implement a revoke/delAgent method — this is a
|
|
91
|
+
* best-effort field set mirroring the ApproveAgent terminal fields (AgentAddress,
|
|
92
|
+
* AsterChain, User, Nonce) without the permission booleans.
|
|
93
|
+
*/
|
|
94
|
+
export declare function buildDelAgentTypedData(params: DelAgentParams): {
|
|
95
|
+
domain: typeof ASTER_DOMAIN_A;
|
|
96
|
+
types: {
|
|
97
|
+
DelAgent: Eip712Field[];
|
|
98
|
+
};
|
|
99
|
+
primaryType: "DelAgent";
|
|
100
|
+
message: Record<string, unknown>;
|
|
101
|
+
};
|
|
102
|
+
/**
|
|
103
|
+
* Encode order parameters as a URL-encoded query string.
|
|
104
|
+
*
|
|
105
|
+
* Deterministic key order = insertion order of `orderParams` (JS object property
|
|
106
|
+
* iteration order). Caller must pre-populate `user` and `nonce` (microseconds)
|
|
107
|
+
* in the dict; this function does NOT inject defaults.
|
|
108
|
+
*
|
|
109
|
+
* Uses `URLSearchParams` for encoding consistency with Python
|
|
110
|
+
* `urllib.parse.urlencode`.
|
|
111
|
+
*/
|
|
112
|
+
export declare function buildOrderQueryStringMsg(orderParams: Record<string, string | number | boolean>): string;
|
|
113
|
+
/**
|
|
114
|
+
* Build the EIP-712 typed-data payload for agent-signed order requests.
|
|
115
|
+
*
|
|
116
|
+
* Domain B (chainId=1666), primaryType="Message", single `msg` field containing
|
|
117
|
+
* the URL-encoded query string produced by buildOrderQueryStringMsg.
|
|
118
|
+
*/
|
|
119
|
+
export declare function buildOrderTypedData(orderParams: Record<string, string | number | boolean>): {
|
|
120
|
+
domain: typeof ASTER_DOMAIN_B;
|
|
121
|
+
types: typeof ORDER_TYPES;
|
|
122
|
+
primaryType: "Message";
|
|
123
|
+
message: {
|
|
124
|
+
msg: string;
|
|
125
|
+
};
|
|
126
|
+
};
|
|
127
|
+
export interface ApproveBuilderParams {
|
|
128
|
+
builder: string;
|
|
129
|
+
maxFeeRate: string;
|
|
130
|
+
builderName?: string;
|
|
131
|
+
user: `0x${string}`;
|
|
132
|
+
nonceMicros: number | string;
|
|
133
|
+
asterChain?: AsterChain;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Build the EIP-712 typed-data payload for `POST /fapi/v3/approveBuilder`.
|
|
137
|
+
*
|
|
138
|
+
* Domain A (chainId=56), primaryType="ApproveBuilder".
|
|
139
|
+
* Default-path field order from HypurrQuant_FE AsterPerpAdapter.ts:630-651:
|
|
140
|
+
* Builder, MaxFeeRate, AsterChain, User, Nonce
|
|
141
|
+
* `BuilderName` is a forward-compatible extension (HQ_FE omits it); included
|
|
142
|
+
* only when explicitly provided so the default path stays byte-equivalent to
|
|
143
|
+
* the production reference.
|
|
144
|
+
*
|
|
145
|
+
* Must be called BEFORE approveAgent so Aster fee attribution is set up
|
|
146
|
+
* before the first order.
|
|
147
|
+
*/
|
|
148
|
+
export declare function buildApproveBuilderTypedData(params: ApproveBuilderParams): {
|
|
149
|
+
domain: typeof ASTER_DOMAIN_A;
|
|
150
|
+
types: {
|
|
151
|
+
ApproveBuilder: Eip712Field[];
|
|
152
|
+
};
|
|
153
|
+
primaryType: "ApproveBuilder";
|
|
154
|
+
message: Record<string, unknown>;
|
|
155
|
+
};
|
|
156
|
+
export {};
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Aster V3 EIP-712 typed-data builders.
|
|
3
|
+
*
|
|
4
|
+
* Pure functions — no side effects, no fs/fetch/env reads.
|
|
5
|
+
* All field ordering and serialization matches the HypurrQuant_FE mainnet
|
|
6
|
+
* production reference: AsterPerpAdapter.ts:456-700.
|
|
7
|
+
*
|
|
8
|
+
* Key conventions:
|
|
9
|
+
* - Domain A (chainId=56) — master-signed ops: approveAgent, delAgent, approveBuilder
|
|
10
|
+
* - Domain B (chainId=1666) — agent-signed per-order ops
|
|
11
|
+
* - uint256 fields serialized as decimal strings (never BigInt) to survive JSON.stringify
|
|
12
|
+
* - EIP-712 message keys are PascalCase; HTTP query-string keys are camelCase
|
|
13
|
+
*/
|
|
14
|
+
// ── Domain constants ──────────────────────────────────────────────────────────
|
|
15
|
+
/** Domain A: used for master-signed ops (ApproveAgent, DelAgent, ApproveBuilder). */
|
|
16
|
+
export const ASTER_DOMAIN_A = {
|
|
17
|
+
name: "AsterSignTransaction",
|
|
18
|
+
version: "1",
|
|
19
|
+
chainId: 56,
|
|
20
|
+
verifyingContract: "0x0000000000000000000000000000000000000000",
|
|
21
|
+
};
|
|
22
|
+
/** Domain B: used for agent-signed per-request ops (order, cancel, leverage). */
|
|
23
|
+
export const ASTER_DOMAIN_B = {
|
|
24
|
+
...ASTER_DOMAIN_A,
|
|
25
|
+
chainId: 1666,
|
|
26
|
+
};
|
|
27
|
+
/** EIP-712 types for Domain B order signing — single msg string (URL-encoded query string). */
|
|
28
|
+
export const ORDER_TYPES = {
|
|
29
|
+
Message: [{ name: "msg", type: "string" }],
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Build the EIP-712 typed-data payload for `POST /fapi/v3/approveAgent`.
|
|
33
|
+
*
|
|
34
|
+
* Field order in types array and message object matches HypurrQuant_FE
|
|
35
|
+
* AsterPerpAdapter.ts:481-533 exactly:
|
|
36
|
+
* AgentName, AgentAddress, [IpWhitelist?,] Expired, CanSpotTrade, CanPerpTrade,
|
|
37
|
+
* CanWithdraw, [Builder?, MaxFeeRate?, BuilderName?,] AsterChain, User, Nonce
|
|
38
|
+
*/
|
|
39
|
+
export function buildApproveAgentTypedData(params) {
|
|
40
|
+
const ipWhitelistIncluded = params.ipWhitelist !== undefined && params.ipWhitelist !== null;
|
|
41
|
+
const builderIncluded = params.builder !== undefined && params.builder !== null;
|
|
42
|
+
// Build types array — insertion order must match message object order.
|
|
43
|
+
const agentTypes = [
|
|
44
|
+
{ name: "AgentName", type: "string" },
|
|
45
|
+
{ name: "AgentAddress", type: "string" },
|
|
46
|
+
];
|
|
47
|
+
if (ipWhitelistIncluded) {
|
|
48
|
+
agentTypes.push({ name: "IpWhitelist", type: "string" });
|
|
49
|
+
}
|
|
50
|
+
agentTypes.push({ name: "Expired", type: "uint256" }, { name: "CanSpotTrade", type: "bool" }, { name: "CanPerpTrade", type: "bool" }, { name: "CanWithdraw", type: "bool" });
|
|
51
|
+
if (builderIncluded) {
|
|
52
|
+
agentTypes.push({ name: "Builder", type: "string" });
|
|
53
|
+
agentTypes.push({ name: "MaxFeeRate", type: "string" });
|
|
54
|
+
if (params.builderName !== undefined) {
|
|
55
|
+
agentTypes.push({ name: "BuilderName", type: "string" });
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
agentTypes.push({ name: "AsterChain", type: "string" }, { name: "User", type: "string" }, { name: "Nonce", type: "uint256" });
|
|
59
|
+
// Build message object — same insertion order as types array.
|
|
60
|
+
const message = {
|
|
61
|
+
AgentName: params.agentName,
|
|
62
|
+
AgentAddress: params.agentAddress,
|
|
63
|
+
};
|
|
64
|
+
if (ipWhitelistIncluded) {
|
|
65
|
+
message["IpWhitelist"] = params.ipWhitelist;
|
|
66
|
+
}
|
|
67
|
+
// uint256 → decimal string (BigInt throws in JSON.stringify inside providers)
|
|
68
|
+
message["Expired"] = String(params.expiredMs);
|
|
69
|
+
message["CanSpotTrade"] = params.canSpotTrade;
|
|
70
|
+
message["CanPerpTrade"] = params.canPerpTrade;
|
|
71
|
+
message["CanWithdraw"] = params.canWithdraw;
|
|
72
|
+
if (builderIncluded) {
|
|
73
|
+
message["Builder"] = params.builder;
|
|
74
|
+
message["MaxFeeRate"] = params.maxFeeRate;
|
|
75
|
+
if (params.builderName !== undefined) {
|
|
76
|
+
message["BuilderName"] = params.builderName;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
message["AsterChain"] = params.asterChain ?? "Mainnet";
|
|
80
|
+
message["User"] = params.user;
|
|
81
|
+
message["Nonce"] = String(params.nonceMicros);
|
|
82
|
+
return {
|
|
83
|
+
domain: ASTER_DOMAIN_A,
|
|
84
|
+
types: { ApproveAgent: agentTypes },
|
|
85
|
+
primaryType: "ApproveAgent",
|
|
86
|
+
message,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Build the EIP-712 typed-data payload for `DELETE /fapi/v3/agent`.
|
|
91
|
+
*
|
|
92
|
+
* TODO: confirm DelAgent struct vs Aster docs at Step 0b spike.
|
|
93
|
+
* HypurrQuant_FE does not implement a revoke/delAgent method — this is a
|
|
94
|
+
* best-effort field set mirroring the ApproveAgent terminal fields (AgentAddress,
|
|
95
|
+
* AsterChain, User, Nonce) without the permission booleans.
|
|
96
|
+
*/
|
|
97
|
+
export function buildDelAgentTypedData(params) {
|
|
98
|
+
const delTypes = [
|
|
99
|
+
{ name: "AgentAddress", type: "string" },
|
|
100
|
+
{ name: "AsterChain", type: "string" },
|
|
101
|
+
{ name: "User", type: "string" },
|
|
102
|
+
{ name: "Nonce", type: "uint256" },
|
|
103
|
+
];
|
|
104
|
+
const message = {
|
|
105
|
+
AgentAddress: params.agentAddress,
|
|
106
|
+
AsterChain: params.asterChain ?? "Mainnet",
|
|
107
|
+
User: params.user,
|
|
108
|
+
Nonce: String(params.nonceMicros),
|
|
109
|
+
};
|
|
110
|
+
return {
|
|
111
|
+
domain: ASTER_DOMAIN_A,
|
|
112
|
+
types: { DelAgent: delTypes },
|
|
113
|
+
primaryType: "DelAgent",
|
|
114
|
+
message,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
// ── buildOrderQueryStringMsg ──────────────────────────────────────────────────
|
|
118
|
+
/**
|
|
119
|
+
* Encode order parameters as a URL-encoded query string.
|
|
120
|
+
*
|
|
121
|
+
* Deterministic key order = insertion order of `orderParams` (JS object property
|
|
122
|
+
* iteration order). Caller must pre-populate `user` and `nonce` (microseconds)
|
|
123
|
+
* in the dict; this function does NOT inject defaults.
|
|
124
|
+
*
|
|
125
|
+
* Uses `URLSearchParams` for encoding consistency with Python
|
|
126
|
+
* `urllib.parse.urlencode`.
|
|
127
|
+
*/
|
|
128
|
+
export function buildOrderQueryStringMsg(orderParams) {
|
|
129
|
+
const sp = new URLSearchParams();
|
|
130
|
+
for (const [key, value] of Object.entries(orderParams)) {
|
|
131
|
+
sp.append(key, String(value));
|
|
132
|
+
}
|
|
133
|
+
return sp.toString();
|
|
134
|
+
}
|
|
135
|
+
// ── buildOrderTypedData ───────────────────────────────────────────────────────
|
|
136
|
+
/**
|
|
137
|
+
* Build the EIP-712 typed-data payload for agent-signed order requests.
|
|
138
|
+
*
|
|
139
|
+
* Domain B (chainId=1666), primaryType="Message", single `msg` field containing
|
|
140
|
+
* the URL-encoded query string produced by buildOrderQueryStringMsg.
|
|
141
|
+
*/
|
|
142
|
+
export function buildOrderTypedData(orderParams) {
|
|
143
|
+
const msg = buildOrderQueryStringMsg(orderParams);
|
|
144
|
+
return {
|
|
145
|
+
domain: ASTER_DOMAIN_B,
|
|
146
|
+
types: ORDER_TYPES,
|
|
147
|
+
primaryType: "Message",
|
|
148
|
+
message: { msg },
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Build the EIP-712 typed-data payload for `POST /fapi/v3/approveBuilder`.
|
|
153
|
+
*
|
|
154
|
+
* Domain A (chainId=56), primaryType="ApproveBuilder".
|
|
155
|
+
* Default-path field order from HypurrQuant_FE AsterPerpAdapter.ts:630-651:
|
|
156
|
+
* Builder, MaxFeeRate, AsterChain, User, Nonce
|
|
157
|
+
* `BuilderName` is a forward-compatible extension (HQ_FE omits it); included
|
|
158
|
+
* only when explicitly provided so the default path stays byte-equivalent to
|
|
159
|
+
* the production reference.
|
|
160
|
+
*
|
|
161
|
+
* Must be called BEFORE approveAgent so Aster fee attribution is set up
|
|
162
|
+
* before the first order.
|
|
163
|
+
*/
|
|
164
|
+
export function buildApproveBuilderTypedData(params) {
|
|
165
|
+
const builderTypes = [
|
|
166
|
+
{ name: "Builder", type: "string" },
|
|
167
|
+
{ name: "MaxFeeRate", type: "string" },
|
|
168
|
+
];
|
|
169
|
+
if (params.builderName !== undefined) {
|
|
170
|
+
builderTypes.push({ name: "BuilderName", type: "string" });
|
|
171
|
+
}
|
|
172
|
+
builderTypes.push({ name: "AsterChain", type: "string" }, { name: "User", type: "string" }, { name: "Nonce", type: "uint256" });
|
|
173
|
+
const message = {
|
|
174
|
+
Builder: params.builder,
|
|
175
|
+
MaxFeeRate: params.maxFeeRate,
|
|
176
|
+
};
|
|
177
|
+
if (params.builderName !== undefined) {
|
|
178
|
+
message["BuilderName"] = params.builderName;
|
|
179
|
+
}
|
|
180
|
+
message["AsterChain"] = params.asterChain ?? "Mainnet";
|
|
181
|
+
message["User"] = params.user;
|
|
182
|
+
message["Nonce"] = String(params.nonceMicros);
|
|
183
|
+
return {
|
|
184
|
+
domain: ASTER_DOMAIN_A,
|
|
185
|
+
types: { ApproveBuilder: builderTypes },
|
|
186
|
+
primaryType: "ApproveBuilder",
|
|
187
|
+
message,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
@@ -1,17 +1,33 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Aster DEX adapter —
|
|
2
|
+
* Aster DEX adapter — EIP-712 three-tier signer routing (v3.2).
|
|
3
|
+
*
|
|
4
|
+
* Signer priority:
|
|
5
|
+
* Tier 1 — Agent OWS wallet (registered + not expired + no --no-agent)
|
|
6
|
+
* Tier 2 — OWS master wallet (--ows / owsActiveWallet)
|
|
7
|
+
* Tier 3 — PK direct (--private-key / PRIVATE_KEY / ASTER_PRIVATE_KEY)
|
|
8
|
+
*
|
|
9
|
+
* HMAC paths fully removed (see Step 3 plan — all Binance-compat HMAC code deleted).
|
|
10
|
+
*
|
|
3
11
|
* Docs: https://docs.asterdex.com/product/aster-perpetuals/api/api-documentation
|
|
4
12
|
* Base: https://fapi.asterdex.com
|
|
5
13
|
*/
|
|
6
14
|
import type { ExchangeAdapter, ExchangeMarketInfo, ExchangePosition, ExchangeOrder, ExchangeBalance, ExchangeTrade, ExchangeFundingPayment, ExchangeKline } from "./interface.js";
|
|
15
|
+
import type { EvmSigner } from "../signer/interface.js";
|
|
16
|
+
import type { AgentMeta } from "../settings.js";
|
|
17
|
+
import type { AgentSigningStrategy } from "../agent-wallet/signing-strategy.js";
|
|
7
18
|
export declare class AsterAdapter implements ExchangeAdapter {
|
|
8
19
|
readonly name = "aster";
|
|
9
20
|
readonly chain = "bnb";
|
|
10
21
|
readonly aliases: readonly ["ast"];
|
|
11
|
-
private _apiKey;
|
|
12
|
-
private _apiSecret;
|
|
13
22
|
private _baseUrl;
|
|
14
23
|
private _testnet;
|
|
24
|
+
private _agentMeta?;
|
|
25
|
+
private _agentSigner?;
|
|
26
|
+
private _masterSigner?;
|
|
27
|
+
private _pkSigner?;
|
|
28
|
+
private _useNoAgent;
|
|
29
|
+
/** PK held between ctor and init() since LocalEvmSigner.create() is async */
|
|
30
|
+
private _pendingPk?;
|
|
15
31
|
private _marketsCache;
|
|
16
32
|
private _marketsCacheTime;
|
|
17
33
|
private _fundingHoursCache;
|
|
@@ -20,14 +36,20 @@ export declare class AsterAdapter implements ExchangeAdapter {
|
|
|
20
36
|
private _ordersCache;
|
|
21
37
|
private static readonly CACHE_TTL;
|
|
22
38
|
private static readonly ACCOUNT_CACHE_TTL;
|
|
23
|
-
|
|
39
|
+
private _nonceCounter;
|
|
40
|
+
constructor(privateKey?: string, testnet?: boolean);
|
|
41
|
+
init(): Promise<void>;
|
|
42
|
+
setAgent(meta: AgentMeta, strategy: AgentSigningStrategy): void;
|
|
43
|
+
setMasterSigner(signer: EvmSigner): void;
|
|
44
|
+
setPkSigner(signer: EvmSigner): void;
|
|
45
|
+
setNoAgent(noAgent: boolean): void;
|
|
24
46
|
get isReadOnly(): boolean;
|
|
47
|
+
get activeSignerTier(): "agent" | "master" | "pk" | null;
|
|
25
48
|
/** CLI symbol → Aster API symbol (ETH → ETHUSDT) */
|
|
26
49
|
private _toApi;
|
|
27
50
|
/** Aster API symbol → CLI symbol (ETHUSDT → ETH) */
|
|
28
51
|
private _fromApi;
|
|
29
|
-
|
|
30
|
-
/** Get funding interval for a symbol (lazy bootstrap — queries on first access, cached permanently). */
|
|
52
|
+
/** Get funding interval for a symbol (lazy bootstrap). */
|
|
31
53
|
getFundingHours(symbol: string): Promise<number | undefined>;
|
|
32
54
|
getMarkets(): Promise<ExchangeMarketInfo[]>;
|
|
33
55
|
getOrderbook(symbol: string): Promise<{
|
|
@@ -66,11 +88,38 @@ export declare class AsterAdapter implements ExchangeAdapter {
|
|
|
66
88
|
assetId?: number;
|
|
67
89
|
routeType?: number;
|
|
68
90
|
}): Promise<unknown>;
|
|
69
|
-
private
|
|
70
|
-
|
|
91
|
+
private _resolveSigner;
|
|
92
|
+
private _nextNonce;
|
|
93
|
+
/**
|
|
94
|
+
* Build the canonical signed-request URL per Aster v3 spec.
|
|
95
|
+
*
|
|
96
|
+
* Reference: https://github.com/asterdex/api-docs (V3, EN, send_by_url).
|
|
97
|
+
* The Python reference adds keys to the dict in this exact order:
|
|
98
|
+
* nonce, user, signer
|
|
99
|
+
* then URL-encodes the dict to form the EIP-712 `msg`. After signing,
|
|
100
|
+
* `&signature=` is appended to the same query string. Authority verification
|
|
101
|
+
* reconstructs `msg` from the URL query params minus `signature`, so the
|
|
102
|
+
* signed dict and the URL query string must be byte-identical except for
|
|
103
|
+
* the appended signature.
|
|
104
|
+
*
|
|
105
|
+
* `signatureChainId` is NOT a v3 parameter — it's an artifact of an older
|
|
106
|
+
* scheme; including it breaks signature verification on every call.
|
|
107
|
+
*/
|
|
108
|
+
private _buildSignedQueryString;
|
|
109
|
+
/**
|
|
110
|
+
* Sign and POST to a path using EIP-712 Domain B.
|
|
111
|
+
*/
|
|
112
|
+
private _signedPostEip712;
|
|
113
|
+
/**
|
|
114
|
+
* Sign and GET from a path using EIP-712 Domain B.
|
|
115
|
+
*/
|
|
116
|
+
private _signedGetEip712;
|
|
117
|
+
/**
|
|
118
|
+
* Sign and DELETE from a path using EIP-712 Domain B.
|
|
119
|
+
*/
|
|
120
|
+
private _signedDeleteEip712;
|
|
121
|
+
private _handleEip712Response;
|
|
122
|
+
/** Handle 429 rate limit with retry — used by GET/DELETE multi-attempt loops */
|
|
71
123
|
private _handleResponse;
|
|
72
124
|
private _publicGet;
|
|
73
|
-
private _signedGet;
|
|
74
|
-
private _signedPost;
|
|
75
|
-
private _signedDelete;
|
|
76
125
|
}
|