otto-intel-mcp 0.1.0
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/LICENSE +21 -0
- package/README.md +102 -0
- package/dist/adapter/cdp-signer.d.ts +133 -0
- package/dist/adapter/cdp-signer.js +356 -0
- package/dist/adapter/cdp-signer.js.map +1 -0
- package/dist/adapter/chain.d.ts +36 -0
- package/dist/adapter/chain.js +65 -0
- package/dist/adapter/chain.js.map +1 -0
- package/dist/adapter/erc20.d.ts +39 -0
- package/dist/adapter/erc20.js +17 -0
- package/dist/adapter/erc20.js.map +1 -0
- package/dist/adapter/lifi-decode.d.ts +52 -0
- package/dist/adapter/lifi-decode.js +149 -0
- package/dist/adapter/lifi-decode.js.map +1 -0
- package/dist/adapter/refusal.d.ts +21 -0
- package/dist/adapter/refusal.js +55 -0
- package/dist/adapter/refusal.js.map +1 -0
- package/dist/adapter/sent-step.d.ts +15 -0
- package/dist/adapter/sent-step.js +6 -0
- package/dist/adapter/sent-step.js.map +1 -0
- package/dist/adapter/verify.d.ts +149 -0
- package/dist/adapter/verify.js +432 -0
- package/dist/adapter/verify.js.map +1 -0
- package/dist/adapter-cdp-index.d.ts +6 -0
- package/dist/adapter-cdp-index.js +7 -0
- package/dist/adapter-index.d.ts +15 -0
- package/dist/adapter-index.js +15 -0
- package/dist/artifact-id.d.ts +16 -0
- package/dist/artifact-id.js +60 -0
- package/dist/boot-redaction.d.ts +16 -0
- package/dist/boot-redaction.js +54 -0
- package/dist/data-source.d.ts +7 -0
- package/dist/data-source.js +2 -0
- package/dist/errors.d.ts +70 -0
- package/dist/errors.js +221 -0
- package/dist/execution-config.d.ts +232 -0
- package/dist/execution-config.js +443 -0
- package/dist/execution-delegated-definition.d.ts +165 -0
- package/dist/execution-delegated-definition.js +116 -0
- package/dist/execution-delegation-admin-definition.d.ts +208 -0
- package/dist/execution-delegation-admin-definition.js +170 -0
- package/dist/execution-delegation-admin.d.ts +74 -0
- package/dist/execution-delegation-admin.js +290 -0
- package/dist/execution-delegation-policy.d.ts +257 -0
- package/dist/execution-delegation-policy.js +279 -0
- package/dist/execution-delegation.d.ts +190 -0
- package/dist/execution-delegation.js +545 -0
- package/dist/execution-errors.d.ts +9 -0
- package/dist/execution-errors.js +128 -0
- package/dist/execution-index.d.ts +17 -0
- package/dist/execution-index.js +16 -0
- package/dist/execution-intent.d.ts +14 -0
- package/dist/execution-intent.js +36 -0
- package/dist/execution-registration.d.ts +113 -0
- package/dist/execution-registration.js +161 -0
- package/dist/execution-tool-definitions.d.ts +1103 -0
- package/dist/execution-tool-definitions.js +1051 -0
- package/dist/execution-tools.d.ts +85 -0
- package/dist/execution-tools.js +690 -0
- package/dist/execution-types.d.ts +274 -0
- package/dist/execution-types.js +157 -0
- package/dist/hyperliquid-info-client.d.ts +31 -0
- package/dist/hyperliquid-info-client.js +121 -0
- package/dist/hyperliquid-order-assertions.d.ts +89 -0
- package/dist/hyperliquid-order-assertions.js +325 -0
- package/dist/hyperliquid-order-builder.d.ts +47 -0
- package/dist/hyperliquid-order-builder.js +55 -0
- package/dist/lifi-bridge-assertions.d.ts +49 -0
- package/dist/lifi-bridge-assertions.js +303 -0
- package/dist/lifi-execution-client.d.ts +74 -0
- package/dist/lifi-execution-client.js +165 -0
- package/dist/lifi-fee-assertions.d.ts +32 -0
- package/dist/lifi-fee-assertions.js +103 -0
- package/dist/lifi-swap-assertions.d.ts +32 -0
- package/dist/lifi-swap-assertions.js +214 -0
- package/dist/local-config.d.ts +9 -0
- package/dist/local-config.js +45 -0
- package/dist/mcp-server.d.ts +4 -0
- package/dist/mcp-server.js +44 -0
- package/dist/polymarket-clob-client.d.ts +73 -0
- package/dist/polymarket-clob-client.js +186 -0
- package/dist/polymarket-order-assertions.d.ts +60 -0
- package/dist/polymarket-order-assertions.js +273 -0
- package/dist/polymarket-order-builder.d.ts +43 -0
- package/dist/polymarket-order-builder.js +74 -0
- package/dist/prepared-artifacts.d.ts +45 -0
- package/dist/prepared-artifacts.js +92 -0
- package/dist/stdio-main.d.ts +2 -0
- package/dist/stdio-main.js +20 -0
- package/dist/tool-definitions.d.ts +20 -0
- package/dist/tool-definitions.js +162 -0
- package/dist/x402-read-source.d.ts +11 -0
- package/dist/x402-read-source.js +77 -0
- package/package.json +162 -0
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import { type Address, type Hex } from 'viem';
|
|
2
|
+
import { type HyperliquidOrderAction, type HyperliquidSigningDeclaration, type PolymarketOrderArgs, type PolymarketOrderTypedData } from './execution-tool-definitions.js';
|
|
3
|
+
export { MAX_ARTIFACT_TTL_MS, artifactIdTimestampMs, createArtifactId } from './artifact-id.js';
|
|
4
|
+
export type AccountKind = 'eoa' | 'safe';
|
|
5
|
+
export type SigningCapability = 'send_evm_transaction' | 'submit_evm_batch' | 'sign_eip712' | 'sign_hyperliquid_l1';
|
|
6
|
+
export interface AccountProfile {
|
|
7
|
+
readonly kind: AccountKind;
|
|
8
|
+
readonly address: Address;
|
|
9
|
+
readonly chainId: number;
|
|
10
|
+
}
|
|
11
|
+
export interface AccountBinding extends AccountProfile {
|
|
12
|
+
readonly protocol_roles: {
|
|
13
|
+
readonly lifi: {
|
|
14
|
+
readonly sender: Address;
|
|
15
|
+
readonly receiver: Address;
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export interface EvmExecutionStep {
|
|
20
|
+
readonly to: Address;
|
|
21
|
+
readonly value: string;
|
|
22
|
+
readonly data: Hex;
|
|
23
|
+
readonly kind: 'approve_reset' | 'approve' | 'call';
|
|
24
|
+
}
|
|
25
|
+
export interface EvmExecutionPlan {
|
|
26
|
+
readonly kind: 'evm_execution_plan';
|
|
27
|
+
readonly chain_id: number;
|
|
28
|
+
readonly steps: readonly EvmExecutionStep[];
|
|
29
|
+
}
|
|
30
|
+
/** The EVM-plan payload variant, shared by the swap and bridge rails. */
|
|
31
|
+
export type ExecutionPayload = EvmExecutionPlan;
|
|
32
|
+
/**
|
|
33
|
+
* The Polymarket payload variant: order args pre-signing plus the exact EIP-712 payload and digest
|
|
34
|
+
* the caller's own signer will sign. No signature, no CLOB credential, no collateral movement.
|
|
35
|
+
*/
|
|
36
|
+
export interface PolymarketOrderPayload {
|
|
37
|
+
readonly kind: 'polymarket_order_v2';
|
|
38
|
+
readonly chain_id: number;
|
|
39
|
+
readonly exchange: Address;
|
|
40
|
+
readonly neg_risk: boolean;
|
|
41
|
+
readonly tick_size: string;
|
|
42
|
+
readonly order_type: 'GTC';
|
|
43
|
+
readonly order: PolymarketOrderArgs;
|
|
44
|
+
readonly eip712: PolymarketOrderTypedData;
|
|
45
|
+
readonly order_hash: Hex;
|
|
46
|
+
}
|
|
47
|
+
export interface ExecutionAssertion {
|
|
48
|
+
readonly id: string;
|
|
49
|
+
readonly result: 'passed';
|
|
50
|
+
}
|
|
51
|
+
export interface ExecutionSubmission {
|
|
52
|
+
readonly method: SigningCapability;
|
|
53
|
+
readonly chain_id: number;
|
|
54
|
+
readonly artifact_id: string;
|
|
55
|
+
readonly payload_digest: Hex;
|
|
56
|
+
/** Commitment over every envelope field except itself, including valid_until. */
|
|
57
|
+
readonly artifact_digest: Hex;
|
|
58
|
+
readonly adapter_owned_fields: readonly ['nonce', 'gas', 'serialization'];
|
|
59
|
+
readonly client_ref?: string;
|
|
60
|
+
}
|
|
61
|
+
export interface ExecutionEnvelope {
|
|
62
|
+
readonly schema_version: '1';
|
|
63
|
+
readonly artifact_id: string;
|
|
64
|
+
readonly rail: 'lifi';
|
|
65
|
+
readonly operation: 'swap';
|
|
66
|
+
readonly intent_digest: Hex;
|
|
67
|
+
readonly account_binding: AccountBinding;
|
|
68
|
+
readonly required_capabilities: readonly SigningCapability[];
|
|
69
|
+
readonly payload: ExecutionPayload;
|
|
70
|
+
readonly fee_attribution: {
|
|
71
|
+
readonly mechanism: 'lifi_integrator_fee';
|
|
72
|
+
readonly config_version: string;
|
|
73
|
+
};
|
|
74
|
+
readonly replay_disclosure: {
|
|
75
|
+
readonly artifact_role: 'construction_aid';
|
|
76
|
+
readonly freshness_boundary: 'valid_until';
|
|
77
|
+
readonly onchain_replay_boundary: 'signer_account_nonce';
|
|
78
|
+
readonly tool_guarantees_single_execution: false;
|
|
79
|
+
};
|
|
80
|
+
readonly assertions: readonly ExecutionAssertion[];
|
|
81
|
+
readonly valid_until: string;
|
|
82
|
+
readonly submission: ExecutionSubmission;
|
|
83
|
+
}
|
|
84
|
+
export interface BridgeAccountBinding extends AccountProfile {
|
|
85
|
+
readonly protocol_roles: {
|
|
86
|
+
readonly lifi: {
|
|
87
|
+
readonly sender: Address;
|
|
88
|
+
/** Where the destination chain mints. For a Safe this is caller-declared, never derived. */
|
|
89
|
+
readonly receiver: Address;
|
|
90
|
+
readonly source_chain_id: number;
|
|
91
|
+
readonly destination_chain_id: number;
|
|
92
|
+
readonly sending_asset: Address;
|
|
93
|
+
readonly receiving_asset: Address;
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
export interface BridgeExecutionEnvelope extends Omit<ExecutionEnvelope, 'operation' | 'account_binding'> {
|
|
98
|
+
readonly operation: 'bridge';
|
|
99
|
+
readonly account_binding: BridgeAccountBinding;
|
|
100
|
+
}
|
|
101
|
+
export interface PolymarketAccountBinding extends AccountProfile {
|
|
102
|
+
readonly protocol_roles: {
|
|
103
|
+
readonly polymarket: {
|
|
104
|
+
/** Source of funds for the order; inside the signed struct (spec §4). */
|
|
105
|
+
readonly maker: Address;
|
|
106
|
+
/** Who signs the EIP-712 order; inside the signed struct. */
|
|
107
|
+
readonly signer: Address;
|
|
108
|
+
/** Who holds the collateral. For the v1 EOA profile all three are the same account. */
|
|
109
|
+
readonly funder: Address;
|
|
110
|
+
readonly exchange: Address;
|
|
111
|
+
readonly token_id: string;
|
|
112
|
+
readonly signature_type: 0;
|
|
113
|
+
};
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
export interface PolymarketExecutionSubmission {
|
|
117
|
+
readonly method: 'sign_eip712';
|
|
118
|
+
readonly chain_id: number;
|
|
119
|
+
readonly artifact_id: string;
|
|
120
|
+
readonly payload_digest: Hex;
|
|
121
|
+
readonly artifact_digest: Hex;
|
|
122
|
+
readonly adapter_owned_fields: readonly ['signature', 'clob_post'];
|
|
123
|
+
readonly client_ref?: string;
|
|
124
|
+
}
|
|
125
|
+
export interface PolymarketExecutionEnvelope extends Omit<ExecutionEnvelope, 'rail' | 'operation' | 'account_binding' | 'payload' | 'fee_attribution' | 'replay_disclosure' | 'required_capabilities' | 'submission'> {
|
|
126
|
+
readonly rail: 'polymarket';
|
|
127
|
+
readonly operation: 'pm_order';
|
|
128
|
+
readonly account_binding: PolymarketAccountBinding;
|
|
129
|
+
readonly required_capabilities: readonly ['sign_eip712'];
|
|
130
|
+
readonly payload: PolymarketOrderPayload;
|
|
131
|
+
readonly fee_attribution: {
|
|
132
|
+
readonly mechanism: 'polymarket_builder_code';
|
|
133
|
+
readonly config_version: string;
|
|
134
|
+
};
|
|
135
|
+
readonly replay_disclosure: {
|
|
136
|
+
readonly artifact_role: 'construction_aid';
|
|
137
|
+
readonly freshness_boundary: 'valid_until';
|
|
138
|
+
/** An order struct carries its own salt, so the exchange's order hash is the replay boundary. */
|
|
139
|
+
readonly onchain_replay_boundary: 'exchange_order_hash';
|
|
140
|
+
readonly tool_guarantees_single_execution: false;
|
|
141
|
+
};
|
|
142
|
+
readonly submission: PolymarketExecutionSubmission;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* The Hyperliquid payload variant: the canonical L1 order action plus the declared EIP-712 signing
|
|
146
|
+
* fields the caller's own signer needs. No nonce, no signature, no derived agent wallet.
|
|
147
|
+
*/
|
|
148
|
+
export interface HyperliquidL1ActionPayload {
|
|
149
|
+
readonly kind: 'hyperliquid_l1_action';
|
|
150
|
+
readonly chain_id: 1337;
|
|
151
|
+
readonly action: HyperliquidOrderAction;
|
|
152
|
+
readonly signing: HyperliquidSigningDeclaration;
|
|
153
|
+
readonly vault_address: Address | null;
|
|
154
|
+
/** Constructor-owned expiry in unix ms, equal to `valid_until` and inside the signed digest. */
|
|
155
|
+
readonly expires_after: number;
|
|
156
|
+
}
|
|
157
|
+
export interface HyperliquidAccountBinding extends AccountProfile {
|
|
158
|
+
readonly protocol_roles: {
|
|
159
|
+
readonly hyperliquid: {
|
|
160
|
+
readonly master: Address;
|
|
161
|
+
readonly trading_signer: Address;
|
|
162
|
+
readonly vault_address: Address | null;
|
|
163
|
+
readonly asset: string;
|
|
164
|
+
readonly asset_id: number;
|
|
165
|
+
readonly sz_decimals: number;
|
|
166
|
+
};
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
export interface HyperliquidExecutionSubmission {
|
|
170
|
+
readonly method: 'sign_hyperliquid_l1';
|
|
171
|
+
readonly chain_id: 1337;
|
|
172
|
+
readonly artifact_id: string;
|
|
173
|
+
readonly payload_digest: Hex;
|
|
174
|
+
readonly artifact_digest: Hex;
|
|
175
|
+
readonly adapter_owned_fields: readonly ['nonce', 'signature', 'exchange_post'];
|
|
176
|
+
readonly client_ref?: string;
|
|
177
|
+
}
|
|
178
|
+
export interface HyperliquidExecutionEnvelope extends Omit<ExecutionEnvelope, 'rail' | 'operation' | 'account_binding' | 'payload' | 'fee_attribution' | 'replay_disclosure' | 'required_capabilities' | 'submission'> {
|
|
179
|
+
readonly rail: 'hyperliquid';
|
|
180
|
+
readonly operation: 'perp_order';
|
|
181
|
+
readonly account_binding: HyperliquidAccountBinding;
|
|
182
|
+
readonly required_capabilities: readonly ['sign_hyperliquid_l1'];
|
|
183
|
+
readonly payload: HyperliquidL1ActionPayload;
|
|
184
|
+
readonly fee_attribution: {
|
|
185
|
+
readonly mechanism: 'hyperliquid_builder_fee';
|
|
186
|
+
readonly config_version: string;
|
|
187
|
+
/** Ruled pending state; the `{b, f}` assert lands with the Step-0 reversal (spec §7.2). */
|
|
188
|
+
readonly status: 'pending_step0_builder_fee_approval';
|
|
189
|
+
};
|
|
190
|
+
readonly replay_disclosure: {
|
|
191
|
+
readonly artifact_role: 'construction_aid';
|
|
192
|
+
readonly freshness_boundary: 'valid_until';
|
|
193
|
+
/** HL's signed digest covers an adapter-chosen nonce AND the constructor's expiresAfter. */
|
|
194
|
+
readonly onchain_replay_boundary: 'signer_nonce_and_expires_after';
|
|
195
|
+
readonly tool_guarantees_single_execution: false;
|
|
196
|
+
};
|
|
197
|
+
readonly submission: HyperliquidExecutionSubmission;
|
|
198
|
+
}
|
|
199
|
+
/** Any artifact this MCP produces. Discriminated by `operation`. */
|
|
200
|
+
export type ExecutionArtifact = ExecutionEnvelope | BridgeExecutionEnvelope | PolymarketExecutionEnvelope | HyperliquidExecutionEnvelope;
|
|
201
|
+
/** Envelope shape before the self-referential artifact digest is stamped into submission. */
|
|
202
|
+
export type UncommittedExecutionEnvelope = Omit<ExecutionEnvelope, 'submission'> & {
|
|
203
|
+
readonly submission: Omit<ExecutionSubmission, 'artifact_digest'> & {
|
|
204
|
+
readonly artifact_digest?: Hex;
|
|
205
|
+
};
|
|
206
|
+
};
|
|
207
|
+
/** Bridge envelope before the self-referential artifact digest is stamped into submission. */
|
|
208
|
+
export type UncommittedBridgeExecutionEnvelope = Omit<BridgeExecutionEnvelope, 'submission'> & {
|
|
209
|
+
readonly submission: Omit<ExecutionSubmission, 'artifact_digest'> & {
|
|
210
|
+
readonly artifact_digest?: Hex;
|
|
211
|
+
};
|
|
212
|
+
};
|
|
213
|
+
/** Polymarket envelope before the self-referential artifact digest is stamped into submission. */
|
|
214
|
+
export type UncommittedPolymarketExecutionEnvelope = Omit<PolymarketExecutionEnvelope, 'submission'> & {
|
|
215
|
+
readonly submission: Omit<PolymarketExecutionSubmission, 'artifact_digest'> & {
|
|
216
|
+
readonly artifact_digest?: Hex;
|
|
217
|
+
};
|
|
218
|
+
};
|
|
219
|
+
/** Hyperliquid envelope before the self-referential artifact digest is stamped into submission. */
|
|
220
|
+
export type UncommittedHyperliquidExecutionEnvelope = Omit<HyperliquidExecutionEnvelope, 'submission'> & {
|
|
221
|
+
readonly submission: Omit<HyperliquidExecutionSubmission, 'artifact_digest'> & {
|
|
222
|
+
readonly artifact_digest?: Hex;
|
|
223
|
+
};
|
|
224
|
+
};
|
|
225
|
+
/** Any envelope shape awaiting its whole-envelope commitment. */
|
|
226
|
+
export type UncommittedExecutionArtifact = UncommittedExecutionEnvelope | UncommittedBridgeExecutionEnvelope | UncommittedPolymarketExecutionEnvelope | UncommittedHyperliquidExecutionEnvelope;
|
|
227
|
+
export declare function canonicalJson(value: unknown): string;
|
|
228
|
+
export declare function executionDigest(value: unknown): Hex;
|
|
229
|
+
export declare function accountBinding(profile: AccountProfile): AccountBinding;
|
|
230
|
+
export declare function bridgeAccountBinding(profile: AccountProfile, route: {
|
|
231
|
+
readonly destinationRecipient: Address;
|
|
232
|
+
readonly destinationChainId: number;
|
|
233
|
+
readonly sendingAsset: Address;
|
|
234
|
+
readonly receivingAsset: Address;
|
|
235
|
+
}): BridgeAccountBinding;
|
|
236
|
+
/**
|
|
237
|
+
* The v1 Polymarket profile is a single EOA in all three roles: it signs the order, it is the maker
|
|
238
|
+
* of record inside the signed struct, and it funds the collateral. A Safe profile would need a
|
|
239
|
+
* signature type (POLY_GNOSIS_SAFE vs POLY_1271) that spec §13 leaves open, so it is refused rather
|
|
240
|
+
* than guessed — which is why this factory takes no separate signer or funder.
|
|
241
|
+
*/
|
|
242
|
+
export declare function polymarketAccountBinding(profile: AccountProfile, order: {
|
|
243
|
+
readonly exchange: Address;
|
|
244
|
+
readonly tokenId: string;
|
|
245
|
+
}): PolymarketAccountBinding;
|
|
246
|
+
/**
|
|
247
|
+
* The v1 Hyperliquid profile binds the master account to the signing EOA. `tradingSigner` may be a
|
|
248
|
+
* DIFFERENT address — the user's own approved API wallet — and is taken as a declared fact: Otto never
|
|
249
|
+
* derives an agent key, which is the custody line spec §11 draws around
|
|
250
|
+
* `trade-execution-agent/src/lib/hyperliquid-client.ts:558`. The vault address is likewise declared,
|
|
251
|
+
* never inferred from the account.
|
|
252
|
+
*/
|
|
253
|
+
export declare function hyperliquidAccountBinding(profile: AccountProfile, market: {
|
|
254
|
+
readonly tradingSigner?: Address;
|
|
255
|
+
readonly vaultAddress?: Address;
|
|
256
|
+
readonly asset: string;
|
|
257
|
+
readonly assetId: number;
|
|
258
|
+
readonly szDecimals: number;
|
|
259
|
+
}): HyperliquidAccountBinding;
|
|
260
|
+
export declare function assertArtifactAccountBinding(artifact: ExecutionArtifact, expected: AccountProfile): void;
|
|
261
|
+
export declare function assertArtifactPayloadDigest(artifact: ExecutionArtifact): void;
|
|
262
|
+
/**
|
|
263
|
+
* Commitment over the whole envelope except the commitment field itself. `valid_until` sits
|
|
264
|
+
* inside it, so freshness is bound to the artifact rather than being an editable side field.
|
|
265
|
+
*/
|
|
266
|
+
export declare function artifactCommitmentDigest(artifact: UncommittedExecutionArtifact): Hex;
|
|
267
|
+
/**
|
|
268
|
+
* Fail-closed commitment check: payload digest, whole-envelope commitment, and the
|
|
269
|
+
* constructor-owned freshness bound derived from the artifact id.
|
|
270
|
+
*/
|
|
271
|
+
export declare function assertArtifactCommitment(artifact: ExecutionArtifact): void;
|
|
272
|
+
/** Strict adapter-side validation, including every envelope cross-field relationship. */
|
|
273
|
+
export declare function assertArtifactEnvelope(artifact: unknown): ExecutionArtifact;
|
|
274
|
+
export declare function assertArtifactFresh(artifact: ExecutionArtifact, nowMs?: number): void;
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { getAddress, keccak256, stringToHex } from 'viem';
|
|
2
|
+
import { isArtifactFreshnessWellFormed } from './artifact-id.js';
|
|
3
|
+
import { refuse } from './execution-errors.js';
|
|
4
|
+
import { EXECUTION_ARTIFACT_SCHEMA, } from './execution-tool-definitions.js';
|
|
5
|
+
export { MAX_ARTIFACT_TTL_MS, artifactIdTimestampMs, createArtifactId } from './artifact-id.js';
|
|
6
|
+
function canonicalize(value) {
|
|
7
|
+
if (value === null || typeof value !== 'object')
|
|
8
|
+
return value;
|
|
9
|
+
if (Array.isArray(value))
|
|
10
|
+
return value.map((item) => canonicalize(item));
|
|
11
|
+
const source = value;
|
|
12
|
+
const sorted = {};
|
|
13
|
+
for (const key of Object.keys(source).sort()) {
|
|
14
|
+
if (source[key] !== undefined)
|
|
15
|
+
sorted[key] = canonicalize(source[key]);
|
|
16
|
+
}
|
|
17
|
+
return sorted;
|
|
18
|
+
}
|
|
19
|
+
export function canonicalJson(value) {
|
|
20
|
+
return JSON.stringify(canonicalize(value));
|
|
21
|
+
}
|
|
22
|
+
export function executionDigest(value) {
|
|
23
|
+
return keccak256(stringToHex(canonicalJson(value)));
|
|
24
|
+
}
|
|
25
|
+
export function accountBinding(profile) {
|
|
26
|
+
const address = getAddress(profile.address);
|
|
27
|
+
return Object.freeze({
|
|
28
|
+
kind: profile.kind,
|
|
29
|
+
address,
|
|
30
|
+
chainId: profile.chainId,
|
|
31
|
+
protocol_roles: Object.freeze({
|
|
32
|
+
lifi: Object.freeze({ sender: address, receiver: address }),
|
|
33
|
+
}),
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
export function bridgeAccountBinding(profile, route) {
|
|
37
|
+
const address = getAddress(profile.address);
|
|
38
|
+
return Object.freeze({
|
|
39
|
+
kind: profile.kind,
|
|
40
|
+
address,
|
|
41
|
+
chainId: profile.chainId,
|
|
42
|
+
protocol_roles: Object.freeze({
|
|
43
|
+
lifi: Object.freeze({
|
|
44
|
+
sender: address,
|
|
45
|
+
receiver: getAddress(route.destinationRecipient),
|
|
46
|
+
source_chain_id: profile.chainId,
|
|
47
|
+
destination_chain_id: route.destinationChainId,
|
|
48
|
+
sending_asset: getAddress(route.sendingAsset),
|
|
49
|
+
receiving_asset: getAddress(route.receivingAsset),
|
|
50
|
+
}),
|
|
51
|
+
}),
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* The v1 Polymarket profile is a single EOA in all three roles: it signs the order, it is the maker
|
|
56
|
+
* of record inside the signed struct, and it funds the collateral. A Safe profile would need a
|
|
57
|
+
* signature type (POLY_GNOSIS_SAFE vs POLY_1271) that spec §13 leaves open, so it is refused rather
|
|
58
|
+
* than guessed — which is why this factory takes no separate signer or funder.
|
|
59
|
+
*/
|
|
60
|
+
export function polymarketAccountBinding(profile, order) {
|
|
61
|
+
const address = getAddress(profile.address);
|
|
62
|
+
return Object.freeze({
|
|
63
|
+
kind: profile.kind,
|
|
64
|
+
address,
|
|
65
|
+
chainId: profile.chainId,
|
|
66
|
+
protocol_roles: Object.freeze({
|
|
67
|
+
polymarket: Object.freeze({
|
|
68
|
+
maker: address,
|
|
69
|
+
signer: address,
|
|
70
|
+
funder: address,
|
|
71
|
+
exchange: getAddress(order.exchange),
|
|
72
|
+
token_id: order.tokenId,
|
|
73
|
+
signature_type: 0,
|
|
74
|
+
}),
|
|
75
|
+
}),
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* The v1 Hyperliquid profile binds the master account to the signing EOA. `tradingSigner` may be a
|
|
80
|
+
* DIFFERENT address — the user's own approved API wallet — and is taken as a declared fact: Otto never
|
|
81
|
+
* derives an agent key, which is the custody line spec §11 draws around
|
|
82
|
+
* `trade-execution-agent/src/lib/hyperliquid-client.ts:558`. The vault address is likewise declared,
|
|
83
|
+
* never inferred from the account.
|
|
84
|
+
*/
|
|
85
|
+
export function hyperliquidAccountBinding(profile, market) {
|
|
86
|
+
const address = getAddress(profile.address);
|
|
87
|
+
return Object.freeze({
|
|
88
|
+
kind: profile.kind,
|
|
89
|
+
address,
|
|
90
|
+
chainId: profile.chainId,
|
|
91
|
+
protocol_roles: Object.freeze({
|
|
92
|
+
hyperliquid: Object.freeze({
|
|
93
|
+
master: address,
|
|
94
|
+
trading_signer: market.tradingSigner ? getAddress(market.tradingSigner) : address,
|
|
95
|
+
vault_address: market.vaultAddress ? getAddress(market.vaultAddress) : null,
|
|
96
|
+
asset: market.asset,
|
|
97
|
+
asset_id: market.assetId,
|
|
98
|
+
sz_decimals: market.szDecimals,
|
|
99
|
+
}),
|
|
100
|
+
}),
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
export function assertArtifactAccountBinding(artifact, expected) {
|
|
104
|
+
const actual = artifact.account_binding;
|
|
105
|
+
if (actual.kind !== expected.kind ||
|
|
106
|
+
actual.chainId !== expected.chainId ||
|
|
107
|
+
getAddress(actual.address) !== getAddress(expected.address)) {
|
|
108
|
+
refuse('ACCOUNT_BINDING_MISMATCH', 'execution artifact is bound to a different account');
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
export function assertArtifactPayloadDigest(artifact) {
|
|
112
|
+
if (artifact.submission.artifact_id !== artifact.artifact_id ||
|
|
113
|
+
executionDigest(artifact.payload) !== artifact.submission.payload_digest) {
|
|
114
|
+
refuse('DIGEST_MISMATCH', 'execution artifact payload digest does not match its payload');
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Commitment over the whole envelope except the commitment field itself. `valid_until` sits
|
|
119
|
+
* inside it, so freshness is bound to the artifact rather than being an editable side field.
|
|
120
|
+
*/
|
|
121
|
+
export function artifactCommitmentDigest(artifact) {
|
|
122
|
+
const { submission, ...envelope } = artifact;
|
|
123
|
+
const { artifact_digest: _committed, ...committedSubmission } = submission;
|
|
124
|
+
return executionDigest({ ...envelope, submission: committedSubmission });
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Fail-closed commitment check: payload digest, whole-envelope commitment, and the
|
|
128
|
+
* constructor-owned freshness bound derived from the artifact id.
|
|
129
|
+
*/
|
|
130
|
+
export function assertArtifactCommitment(artifact) {
|
|
131
|
+
assertArtifactPayloadDigest(artifact);
|
|
132
|
+
if (artifactCommitmentDigest(artifact) !== artifact.submission.artifact_digest) {
|
|
133
|
+
refuse('DIGEST_MISMATCH', 'execution artifact digest does not commit to this envelope');
|
|
134
|
+
}
|
|
135
|
+
if (!isArtifactFreshnessWellFormed(artifact.artifact_id, artifact.valid_until)) {
|
|
136
|
+
refuse('ARTIFACT_INVALID', 'execution artifact expiry is outside the constructor bound');
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/** Strict adapter-side validation, including every envelope cross-field relationship. */
|
|
140
|
+
export function assertArtifactEnvelope(artifact) {
|
|
141
|
+
const parsed = EXECUTION_ARTIFACT_SCHEMA.safeParse(artifact);
|
|
142
|
+
if (!parsed.success) {
|
|
143
|
+
return refuse('ARTIFACT_INVALID', 'execution artifact failed strict envelope validation');
|
|
144
|
+
}
|
|
145
|
+
const envelope = parsed.data;
|
|
146
|
+
assertArtifactCommitment(envelope);
|
|
147
|
+
return envelope;
|
|
148
|
+
}
|
|
149
|
+
export function assertArtifactFresh(artifact, nowMs = Date.now()) {
|
|
150
|
+
// Fail-closed first: the commitment check binds valid_until to the artifact and rejects any
|
|
151
|
+
// expiry outside the construction-anchored ceiling, so the deadline below is constructor-owned.
|
|
152
|
+
assertArtifactCommitment(artifact);
|
|
153
|
+
if (Date.parse(artifact.valid_until) <= nowMs) {
|
|
154
|
+
refuse('STALE_ARTIFACT', 'execution artifact has expired');
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
//# sourceMappingURL=execution-types.js.map
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { ExecutionServiceConfig, HyperliquidPerpAsset } from './execution-config.js';
|
|
3
|
+
/** The live `POST /info {"type":"meta"}` body, as captured in `test/fixtures/hyperliquid-meta.json`. */
|
|
4
|
+
export declare const HYPERLIQUID_META_RESPONSE_SCHEMA: z.ZodObject<{
|
|
5
|
+
universe: z.ZodArray<z.ZodObject<{
|
|
6
|
+
name: z.ZodString;
|
|
7
|
+
szDecimals: z.ZodNumber;
|
|
8
|
+
maxLeverage: z.ZodNumber;
|
|
9
|
+
isDelisted: z.ZodOptional<z.ZodBoolean>;
|
|
10
|
+
}, z.core.$loose>>;
|
|
11
|
+
}, z.core.$loose>;
|
|
12
|
+
export interface HyperliquidInfoClient {
|
|
13
|
+
getPerpAsset(symbol: string, signal?: AbortSignal): Promise<HyperliquidPerpAsset>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Resolve one perp symbol against a live `meta` body. Shared by the REST client and the recorded
|
|
17
|
+
* fixture loader, so the assertions in tests run over the same parse the network path uses.
|
|
18
|
+
*
|
|
19
|
+
* The symbol match is exact and case-sensitive against the venue's own `name`: the universe contains
|
|
20
|
+
* names that differ only by prefix conventions (`kPEPE` vs `PEPE`), and a case-folded match would let
|
|
21
|
+
* a caller's typo resolve to a different market than the one they named.
|
|
22
|
+
*/
|
|
23
|
+
export declare function hyperliquidPerpAsset(raw: unknown, symbol: string): HyperliquidPerpAsset;
|
|
24
|
+
export declare function buildHyperliquidInfoUrl(config: Pick<ExecutionServiceConfig, 'hyperliquidApiBaseUrl'>): URL;
|
|
25
|
+
export declare class RestHyperliquidInfoClient implements HyperliquidInfoClient {
|
|
26
|
+
private readonly config;
|
|
27
|
+
private readonly fetchImpl;
|
|
28
|
+
constructor(config: ExecutionServiceConfig, fetchImpl?: typeof fetch);
|
|
29
|
+
getPerpAsset(symbol: string, signal?: AbortSignal): Promise<HyperliquidPerpAsset>;
|
|
30
|
+
private postInfo;
|
|
31
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { isExecutionRefusal, refuse } from './execution-errors.js';
|
|
3
|
+
/**
|
|
4
|
+
* Public, unauthenticated Hyperliquid info reads only (spec §7.2). This client holds no key, derives
|
|
5
|
+
* no agent wallet, and never touches `/exchange`: the write surface, the root-secret-derived agent
|
|
6
|
+
* (`trade-execution-agent/src/lib/hyperliquid-client.ts:558`) and the Dynamic-vault lifecycle are
|
|
7
|
+
* fenced out of Step 1 by spec §11.
|
|
8
|
+
*
|
|
9
|
+
* One read is needed and only one: `{"type":"meta"}`, whose `universe` array is the authoritative
|
|
10
|
+
* source of the asset index (`a` in the order action), `szDecimals`, and the delisting flag. The
|
|
11
|
+
* index is POSITIONAL — asset id IS the array position — so it can only come from a live read.
|
|
12
|
+
*/
|
|
13
|
+
const PERP_UNIVERSE_ASSET_SCHEMA = z
|
|
14
|
+
.object({
|
|
15
|
+
name: z.string().min(1),
|
|
16
|
+
// 0 is a valid, common value (DOGE, XRP, kPEPE), so absence must not fall back to a truthy
|
|
17
|
+
// default: a wrong szDecimals silently changes both the size grid and the price grid.
|
|
18
|
+
szDecimals: z.number().int().min(0).max(18),
|
|
19
|
+
maxLeverage: z.number().int().positive(),
|
|
20
|
+
isDelisted: z.boolean().optional(),
|
|
21
|
+
})
|
|
22
|
+
.passthrough();
|
|
23
|
+
/** The live `POST /info {"type":"meta"}` body, as captured in `test/fixtures/hyperliquid-meta.json`. */
|
|
24
|
+
export const HYPERLIQUID_META_RESPONSE_SCHEMA = z
|
|
25
|
+
.object({
|
|
26
|
+
universe: z.array(PERP_UNIVERSE_ASSET_SCHEMA).min(1),
|
|
27
|
+
})
|
|
28
|
+
.passthrough();
|
|
29
|
+
function metaRefuse(message) {
|
|
30
|
+
return refuse('HYPERLIQUID_META_INVALID', message);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Resolve one perp symbol against a live `meta` body. Shared by the REST client and the recorded
|
|
34
|
+
* fixture loader, so the assertions in tests run over the same parse the network path uses.
|
|
35
|
+
*
|
|
36
|
+
* The symbol match is exact and case-sensitive against the venue's own `name`: the universe contains
|
|
37
|
+
* names that differ only by prefix conventions (`kPEPE` vs `PEPE`), and a case-folded match would let
|
|
38
|
+
* a caller's typo resolve to a different market than the one they named.
|
|
39
|
+
*/
|
|
40
|
+
export function hyperliquidPerpAsset(raw, symbol) {
|
|
41
|
+
const parsed = HYPERLIQUID_META_RESPONSE_SCHEMA.safeParse(raw);
|
|
42
|
+
if (!parsed.success) {
|
|
43
|
+
metaRefuse('Hyperliquid perp meta is missing a well-formed universe');
|
|
44
|
+
}
|
|
45
|
+
const universe = parsed.data.universe;
|
|
46
|
+
const index = universe.findIndex((asset) => asset.name === symbol);
|
|
47
|
+
if (index < 0) {
|
|
48
|
+
refuse('UNSUPPORTED_HYPERLIQUID_MARKET', `perp ${symbol} is not in the live Hyperliquid perp universe`);
|
|
49
|
+
}
|
|
50
|
+
const asset = universe[index];
|
|
51
|
+
// Only native perps are in scope: a HIP-3 builder-deployed asset lives in a separate `perpDexs`
|
|
52
|
+
// universe whose asset ids are offset (100000 + dexIndex * 10000 + assetIndex), so a positional
|
|
53
|
+
// index taken from THIS universe would name a different market entirely.
|
|
54
|
+
return Object.freeze({
|
|
55
|
+
name: asset.name,
|
|
56
|
+
assetId: index,
|
|
57
|
+
szDecimals: asset.szDecimals,
|
|
58
|
+
maxLeverage: asset.maxLeverage,
|
|
59
|
+
isDelisted: asset.isDelisted === true,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
export function buildHyperliquidInfoUrl(config) {
|
|
63
|
+
return new URL(`${config.hyperliquidApiBaseUrl}/info`);
|
|
64
|
+
}
|
|
65
|
+
function boundedSignal(callerSignal, timeoutMs) {
|
|
66
|
+
const controller = new AbortController();
|
|
67
|
+
const timeout = setTimeout(() => controller.abort(new Error('Hyperliquid info read timeout')), timeoutMs);
|
|
68
|
+
const onAbort = () => controller.abort(callerSignal?.reason);
|
|
69
|
+
if (callerSignal?.aborted)
|
|
70
|
+
onAbort();
|
|
71
|
+
else
|
|
72
|
+
callerSignal?.addEventListener('abort', onAbort, { once: true });
|
|
73
|
+
return {
|
|
74
|
+
signal: controller.signal,
|
|
75
|
+
cleanup: () => {
|
|
76
|
+
clearTimeout(timeout);
|
|
77
|
+
callerSignal?.removeEventListener('abort', onAbort);
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
export class RestHyperliquidInfoClient {
|
|
82
|
+
config;
|
|
83
|
+
fetchImpl;
|
|
84
|
+
constructor(config, fetchImpl = fetch) {
|
|
85
|
+
this.config = config;
|
|
86
|
+
this.fetchImpl = fetchImpl;
|
|
87
|
+
}
|
|
88
|
+
async getPerpAsset(symbol, signal) {
|
|
89
|
+
return hyperliquidPerpAsset(await this.postInfo({ type: 'meta' }, signal), symbol);
|
|
90
|
+
}
|
|
91
|
+
async postInfo(body, callerSignal) {
|
|
92
|
+
const abort = boundedSignal(callerSignal, this.config.hyperliquidTimeoutMs);
|
|
93
|
+
try {
|
|
94
|
+
// No credential header of any kind: `/info` is a public endpoint.
|
|
95
|
+
const response = await this.fetchImpl(buildHyperliquidInfoUrl(this.config), {
|
|
96
|
+
method: 'POST',
|
|
97
|
+
headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
|
|
98
|
+
body: JSON.stringify(body),
|
|
99
|
+
signal: abort.signal,
|
|
100
|
+
});
|
|
101
|
+
if (!response.ok) {
|
|
102
|
+
refuse('HYPERLIQUID_META_FAILED', `Hyperliquid info read failed with HTTP ${response.status}`);
|
|
103
|
+
}
|
|
104
|
+
try {
|
|
105
|
+
return await response.json();
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
refuse('HYPERLIQUID_META_FAILED', 'Hyperliquid info response was not valid JSON');
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
if (isExecutionRefusal(error))
|
|
113
|
+
throw error;
|
|
114
|
+
refuse('HYPERLIQUID_META_FAILED', 'Hyperliquid info read failed');
|
|
115
|
+
}
|
|
116
|
+
finally {
|
|
117
|
+
abort.cleanup();
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=hyperliquid-info-client.js.map
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { type HyperliquidOrderGrouping, type HyperliquidPerpAsset, type HyperliquidTimeInForce } from './execution-config.js';
|
|
2
|
+
import type { HyperliquidOrderAction, HyperliquidSigningDeclaration } from './execution-tool-definitions.js';
|
|
3
|
+
import type { ExecutionAssertion } from './execution-types.js';
|
|
4
|
+
import type { UnsignedHyperliquidAction } from './hyperliquid-order-builder.js';
|
|
5
|
+
export interface HyperliquidDecimal {
|
|
6
|
+
readonly raw: string;
|
|
7
|
+
readonly decimals: number;
|
|
8
|
+
readonly isInteger: boolean;
|
|
9
|
+
/** Digits from the first non-zero digit through the last; the venue's 5-sig-fig rule counts these. */
|
|
10
|
+
readonly significantDigits: number;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Parse an exact decimal string. The caller's string form is authoritative and is what lands in the
|
|
14
|
+
* signed action verbatim: no float ever touches a price or a size here.
|
|
15
|
+
*
|
|
16
|
+
* This is the deliberate divergence from the trade-execution agent, which formats prices through
|
|
17
|
+
* `parseFloat(price.toPrecision(5))` and `Math.floor(price * 10 ** d) / 10 ** d`
|
|
18
|
+
* (`hyperliquid-client.ts:55,1291`) and sizes through `size.toFixed(szDecimals)`. Those ROUND, and two
|
|
19
|
+
* of them round differently from each other. Rounding is acceptable when a caller's intent is "fill me
|
|
20
|
+
* around here" and the round trip is milliseconds; it is not acceptable in a prepare-only artifact the
|
|
21
|
+
* caller signs later, because the price they signed would not be the price they named. So the grid is
|
|
22
|
+
* extracted as a VALIDATION and an off-grid request is refused by name.
|
|
23
|
+
*/
|
|
24
|
+
export declare function hyperliquidDecimal(value: string): HyperliquidDecimal;
|
|
25
|
+
/** Exact scaled integer for comparison. Refuses rather than truncating excess precision. */
|
|
26
|
+
export declare function hyperliquidScaledUnits(value: string, decimals: number): bigint;
|
|
27
|
+
/** Fractional digits the venue accepts in a perp price for this asset: `MAX_DECIMALS - szDecimals`. */
|
|
28
|
+
export declare function hyperliquidMaxPriceDecimals(szDecimals: number): number;
|
|
29
|
+
/**
|
|
30
|
+
* The venue's perp price grid: at most `MAX_DECIMALS - szDecimals` fractional digits AND at most 5
|
|
31
|
+
* significant digits, with integer prices always accepted regardless of significant figures.
|
|
32
|
+
*/
|
|
33
|
+
export declare function assertHyperliquidPriceOnGrid(price: string, szDecimals: number): HyperliquidDecimal;
|
|
34
|
+
/** The venue's size grid: at most `szDecimals` fractional digits, strictly positive. */
|
|
35
|
+
export declare function assertHyperliquidSizeOnGrid(size: string, szDecimals: number): HyperliquidDecimal;
|
|
36
|
+
/**
|
|
37
|
+
* A take-profit below the entry on a long — or above it on a short — is not a take-profit: it is an
|
|
38
|
+
* instruction to close at a loss the moment the entry fills, and the same inversion on the stop-loss
|
|
39
|
+
* leg arms an immediate close. The venue accepts both, and the trade-execution agent does not check
|
|
40
|
+
* either (`hyperliquid-client.ts:1829,1851`). An inverted bracket is a defect in the REQUEST, so
|
|
41
|
+
* naming it is P0-clean (§7.0): a coherent bracket always constructs.
|
|
42
|
+
*/
|
|
43
|
+
export declare function assertHyperliquidBracketCoherent(input: {
|
|
44
|
+
readonly isBuy: boolean;
|
|
45
|
+
readonly price: string;
|
|
46
|
+
readonly takeProfitPrice?: string;
|
|
47
|
+
readonly stopLossPrice?: string;
|
|
48
|
+
readonly szDecimals: number;
|
|
49
|
+
}): void;
|
|
50
|
+
export interface HyperliquidLegExpectation {
|
|
51
|
+
readonly role: 'entry' | 'take_profit' | 'stop_loss';
|
|
52
|
+
readonly isBuy: boolean;
|
|
53
|
+
readonly price: string;
|
|
54
|
+
readonly size: string;
|
|
55
|
+
readonly reduceOnly: boolean;
|
|
56
|
+
readonly tif?: HyperliquidTimeInForce;
|
|
57
|
+
readonly trigger?: {
|
|
58
|
+
readonly triggerPx: string;
|
|
59
|
+
readonly tpsl: 'tp' | 'sl';
|
|
60
|
+
};
|
|
61
|
+
readonly clientOrderId: string;
|
|
62
|
+
}
|
|
63
|
+
export interface HyperliquidActionAssertionInput {
|
|
64
|
+
readonly built: UnsignedHyperliquidAction;
|
|
65
|
+
readonly asset: HyperliquidPerpAsset;
|
|
66
|
+
readonly grouping: HyperliquidOrderGrouping;
|
|
67
|
+
readonly legs: readonly HyperliquidLegExpectation[];
|
|
68
|
+
}
|
|
69
|
+
export interface AssertedHyperliquidAction {
|
|
70
|
+
readonly action: HyperliquidOrderAction;
|
|
71
|
+
readonly assertions: readonly ExecutionAssertion[];
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Decode the normalizer's output back into a strictly typed action and assert every fund-critical
|
|
75
|
+
* field: the positional asset id, the exact price and size strings, the side, reduce-only, the trigger
|
|
76
|
+
* shape, the per-leg client order ids, the grouping, the canonical key order, and the ABSENCE of the
|
|
77
|
+
* builder key (the ruled valueless state, spec §7.2).
|
|
78
|
+
*/
|
|
79
|
+
export declare function decodeAndAssertHyperliquidAction(input: HyperliquidActionAssertionInput): AssertedHyperliquidAction;
|
|
80
|
+
/**
|
|
81
|
+
* The signing declaration: exactly the domain, struct, primary type, and `source` the venue's own
|
|
82
|
+
* signer uses (`@nktkas/hyperliquid` signing/mod.js:170-189), plus the `connectionId` recipe.
|
|
83
|
+
*
|
|
84
|
+
* `connectionId` itself is NOT published, and cannot be: it is
|
|
85
|
+
* `keccak256(msgpack(action) ++ nonce ++ vault ++ expiresAfter)` and the nonce is adapter-owned
|
|
86
|
+
* (§6/§10). Pre-committing a nonce here is precisely what would turn a correct artifact into a stale
|
|
87
|
+
* or replayable one, so the artifact publishes the preimage layout and leaves the nonce to the signer.
|
|
88
|
+
*/
|
|
89
|
+
export declare function hyperliquidSigningDeclaration(): HyperliquidSigningDeclaration;
|