@exagent/agent 0.3.5 → 0.3.6

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.
@@ -1,374 +0,0 @@
1
- /**
2
- * Live Signing Validation Test
3
- *
4
- * Tests our signing/auth code against REAL mainnet APIs.
5
- * No funds needed — we validate that signatures are accepted
6
- * (errors should be "insufficient margin", NOT "invalid signature").
7
- *
8
- * Tests:
9
- * 1. Hyperliquid: EIP-712 sign → submit order → expect margin error (not signature error)
10
- * 2. Polymarket: L2 API key derivation → expect success (proves auth works)
11
- */
12
-
13
- import { createWalletClient, http } from 'viem';
14
- import { privateKeyToAccount } from 'viem/accounts';
15
- import { arbitrum } from 'viem/chains';
16
-
17
- // Test wallet (no funds — that's fine for signing validation)
18
- const TEST_PKEY = '0xf1a60fbdb6a77340ce20756f95496b477ea86ab73e01f6c27a503771a80850ad';
19
-
20
- const account = privateKeyToAccount(TEST_PKEY);
21
- console.log(`Test wallet: ${account.address}`);
22
- console.log('═══════════════════════════════════════════════\n');
23
-
24
- let passed = 0;
25
- let failed = 0;
26
-
27
- // ═══════════════════════════════════════════════════════════
28
- // TEST 1: Hyperliquid EIP-712 Signing
29
- // ═══════════════════════════════════════════════════════════
30
-
31
- console.log('TEST 1: Hyperliquid EIP-712 Signing + Order Submission');
32
- console.log('─────────────────────────────────────────────');
33
-
34
- try {
35
- // Import our modules from dist
36
- const { HyperliquidClient, HyperliquidSigner, getNextNonce, HYPERLIQUID_DOMAIN } = await import('./dist/index.js');
37
-
38
- const config = {
39
- enabled: true,
40
- apiUrl: 'https://api.hyperliquid.xyz',
41
- wsUrl: 'wss://api.hyperliquid.xyz/ws',
42
- maxLeverage: 10,
43
- maxNotionalUSD: 50000,
44
- };
45
-
46
- const client = new HyperliquidClient(config);
47
-
48
- // Create a viem WalletClient for signing
49
- const walletClient = createWalletClient({
50
- account,
51
- chain: arbitrum,
52
- transport: http('https://arb1.arbitrum.io/rpc'),
53
- });
54
-
55
- const signer = new HyperliquidSigner(walletClient);
56
-
57
- // Verify signer address
58
- const signerAddr = signer.getAddress();
59
- console.log(` Signer address: ${signerAddr}`);
60
- if (signerAddr.toLowerCase() !== account.address.toLowerCase()) {
61
- throw new Error('Signer address mismatch!');
62
- }
63
- console.log(' ✓ Signer address matches\n');
64
-
65
- // Get ETH asset index
66
- const ethIndex = await client.getAssetIndex('ETH');
67
- console.log(` ETH asset index: ${ethIndex}`);
68
-
69
- // Simulate the exact same flow as OrderManager.placeOrder
70
- // (price.toString() on a round number produces "2000", not "2000.0")
71
- const signal = { price: 2000, size: 0.001, reduceOnly: false };
72
- const orderWire = {
73
- a: ethIndex,
74
- b: true, // buy (long)
75
- p: signal.price.toString(), // "2000" — correct format
76
- s: signal.size.toString(), // "0.001"
77
- r: signal.reduceOnly,
78
- t: { limit: { tif: 'Gtc' } },
79
- };
80
-
81
- const action = {
82
- type: 'order',
83
- orders: [orderWire],
84
- grouping: 'na',
85
- };
86
-
87
- // Sign the action
88
- const nonce = getNextNonce();
89
- const { signature } = await signer.signAction(action, nonce);
90
- console.log(` Signature: ${signature.slice(0, 20)}...${signature.slice(-8)}`);
91
- console.log(` Nonce: ${nonce}\n`);
92
-
93
- // Parse signature into r, s, v
94
- const sigR = signature.slice(0, 66);
95
- const sigS = `0x${signature.slice(66, 130)}`;
96
- const sigV = parseInt(signature.slice(130, 132), 16);
97
-
98
- console.log(' Submitting to Hyperliquid Exchange API...');
99
-
100
- // Submit to the Exchange API
101
- const resp = await fetch('https://api.hyperliquid.xyz/exchange', {
102
- method: 'POST',
103
- headers: { 'Content-Type': 'application/json' },
104
- body: JSON.stringify({
105
- action,
106
- nonce: Number(nonce),
107
- signature: { r: sigR, s: sigS, v: sigV },
108
- vaultAddress: null,
109
- }),
110
- });
111
-
112
- const respText = await resp.text();
113
- let respJson;
114
- try { respJson = JSON.parse(respText); } catch { respJson = respText; }
115
-
116
- console.log(` HTTP Status: ${resp.status}`);
117
- console.log(` Response: ${JSON.stringify(respJson)}\n`);
118
-
119
- // Analyze the response
120
- const respStr = typeof respJson === 'string' ? respJson : JSON.stringify(respJson);
121
-
122
- if (respStr.toLowerCase().includes('invalid signature') ||
123
- respStr.toLowerCase().includes('signature verification') ||
124
- respStr.toLowerCase().includes('bad signature')) {
125
- console.log(' ✗ SIGNATURE REJECTED — our EIP-712 signing is broken!\n');
126
- failed++;
127
- } else if (resp.status === 200 || resp.status === 400 || resp.status === 403) {
128
- // Any response that ISN'T a signature error means our signing works
129
- // Common expected errors: "Not enough margin", "User does not exist", etc.
130
- console.log(' ✓ Signature ACCEPTED by Hyperliquid API');
131
- console.log(' (Order rejected for business reason, not signing — this is expected)\n');
132
- passed++;
133
- } else {
134
- console.log(` ⚠ Unexpected response — needs manual review\n`);
135
- // Still count as pass if not a signature error
136
- if (!respStr.toLowerCase().includes('signature')) {
137
- passed++;
138
- } else {
139
- failed++;
140
- }
141
- }
142
-
143
- } catch (error) {
144
- console.log(` ✗ Error: ${error.message}\n`);
145
- // Check if the error is about signing or about something else
146
- if (error.message.toLowerCase().includes('signature')) {
147
- failed++;
148
- } else {
149
- console.log(' (Error is not signature-related — may be a network or other issue)');
150
- failed++;
151
- }
152
- }
153
-
154
- // ═══════════════════════════════════════════════════════════
155
- // TEST 2: Hyperliquid Update Leverage (different action type)
156
- // ═══════════════════════════════════════════════════════════
157
-
158
- console.log('TEST 2: Hyperliquid Update Leverage Signing');
159
- console.log('─────────────────────────────────────────────');
160
-
161
- try {
162
- const { HyperliquidClient, HyperliquidSigner, getNextNonce } = await import('./dist/index.js');
163
-
164
- const config = {
165
- enabled: true,
166
- apiUrl: 'https://api.hyperliquid.xyz',
167
- wsUrl: 'wss://api.hyperliquid.xyz/ws',
168
- maxLeverage: 10,
169
- maxNotionalUSD: 50000,
170
- };
171
-
172
- const client = new HyperliquidClient(config);
173
- const walletClient = createWalletClient({
174
- account,
175
- chain: arbitrum,
176
- transport: http('https://arb1.arbitrum.io/rpc'),
177
- });
178
- const signer = new HyperliquidSigner(walletClient);
179
-
180
- const ethIndex = await client.getAssetIndex('ETH');
181
-
182
- const action = {
183
- type: 'updateLeverage',
184
- asset: ethIndex,
185
- isCross: true,
186
- leverage: 5,
187
- };
188
-
189
- const nonce = getNextNonce();
190
- const { signature } = await signer.signAction(action, nonce);
191
-
192
- const sigR = signature.slice(0, 66);
193
- const sigS = `0x${signature.slice(66, 130)}`;
194
- const sigV = parseInt(signature.slice(130, 132), 16);
195
-
196
- console.log(' Submitting updateLeverage to Exchange API...');
197
-
198
- const resp = await fetch('https://api.hyperliquid.xyz/exchange', {
199
- method: 'POST',
200
- headers: { 'Content-Type': 'application/json' },
201
- body: JSON.stringify({
202
- action,
203
- nonce: Number(nonce),
204
- signature: { r: sigR, s: sigS, v: sigV },
205
- vaultAddress: null,
206
- }),
207
- });
208
-
209
- const respText = await resp.text();
210
- let respJson;
211
- try { respJson = JSON.parse(respText); } catch { respJson = respText; }
212
-
213
- console.log(` HTTP Status: ${resp.status}`);
214
- console.log(` Response: ${JSON.stringify(respJson)}\n`);
215
-
216
- const respStr = typeof respJson === 'string' ? respJson : JSON.stringify(respJson);
217
-
218
- if (respStr.toLowerCase().includes('invalid signature') ||
219
- respStr.toLowerCase().includes('bad signature')) {
220
- console.log(' ✗ SIGNATURE REJECTED\n');
221
- failed++;
222
- } else {
223
- console.log(' ✓ Leverage update accepted (or rejected for non-signature reason)\n');
224
- passed++;
225
- }
226
- } catch (error) {
227
- console.log(` ✗ Error: ${error.message}\n`);
228
- failed++;
229
- }
230
-
231
- // ═══════════════════════════════════════════════════════════
232
- // TEST 3: Polymarket L2 API Key Derivation
233
- // ═══════════════════════════════════════════════════════════
234
-
235
- console.log('TEST 3: Polymarket CLOB API Key Derivation');
236
- console.log('─────────────────────────────────────────────');
237
-
238
- try {
239
- const { PolymarketClient } = await import('./dist/index.js');
240
-
241
- console.log(' Initializing PolymarketClient...');
242
- console.log(' (This derives L2 API keys via wallet signature — proves auth works)\n');
243
-
244
- const pmClient = new PolymarketClient(TEST_PKEY, {
245
- enabled: true,
246
- clobApiUrl: 'https://clob.polymarket.com',
247
- gammaApiUrl: 'https://gamma-api.polymarket.com',
248
- maxNotionalUSD: 100,
249
- maxTotalExposureUSD: 200,
250
- });
251
-
252
- await pmClient.initialize();
253
-
254
- if (pmClient.isInitialized) {
255
- console.log(` ✓ CLOB client initialized successfully`);
256
- console.log(` ✓ L2 API keys derived — authentication pipeline WORKS`);
257
- console.log(` Wallet: ${pmClient.getWalletAddress()}\n`);
258
- passed++;
259
-
260
- // Test 3b: Try to fetch open orders (validates L2 auth headers)
261
- console.log(' Fetching open orders (validates L2 auth headers)...');
262
- const orders = await pmClient.getOpenOrders();
263
- console.log(` ✓ Open orders response: ${orders.length} orders (expected: 0)\n`);
264
-
265
- // Test 3c: Try to fetch trade history
266
- console.log(' Fetching trade history...');
267
- const trades = await pmClient.getTradeHistory();
268
- console.log(` ✓ Trade history response: ${trades.length} trades (expected: 0)\n`);
269
-
270
- passed++; // Auth headers work
271
-
272
- } else {
273
- console.log(' ✗ CLOB client failed to initialize\n');
274
- failed++;
275
- }
276
- } catch (error) {
277
- console.log(` ✗ Error: ${error.message}`);
278
- if (error.message.includes('401') || error.message.includes('403')) {
279
- console.log(' (Auth rejected — signing may be incompatible)\n');
280
- } else {
281
- console.log(' (May be a network or API issue)\n');
282
- }
283
- failed++;
284
- }
285
-
286
- // ═══════════════════════════════════════════════════════════
287
- // TEST 4: Polymarket Order Signing (expect insufficient balance)
288
- // ═══════════════════════════════════════════════════════════
289
-
290
- console.log('TEST 4: Polymarket Order Signing');
291
- console.log('─────────────────────────────────────────────');
292
-
293
- try {
294
- const { PolymarketClient } = await import('./dist/index.js');
295
-
296
- const pmClient = new PolymarketClient(TEST_PKEY, {
297
- enabled: true,
298
- clobApiUrl: 'https://clob.polymarket.com',
299
- gammaApiUrl: 'https://gamma-api.polymarket.com',
300
- maxNotionalUSD: 100,
301
- maxTotalExposureUSD: 200,
302
- });
303
-
304
- await pmClient.initialize();
305
-
306
- // Find a real active market to test against
307
- console.log(' Finding an active market (trending)...');
308
- const trending = await pmClient.getTrendingMarkets(1);
309
-
310
- if (trending.length === 0) {
311
- console.log(' ⚠ No active markets found — skipping order test\n');
312
- // Not a failure, just can't test this part
313
- } else {
314
- const market = trending[0];
315
- console.log(` Found: "${market.question}"`);
316
- console.log(` Token IDs: ${market.outcomeTokenIds.join(', ')}`);
317
-
318
- const tokenId = market.outcomeTokenIds[0];
319
- if (tokenId) {
320
- console.log(`\n Attempting to place a tiny limit order (will fail with balance error)...`);
321
- try {
322
- const result = await pmClient.placeLimitOrder({
323
- tokenId,
324
- price: 0.01, // Minimum price
325
- size: 5, // Minimum size
326
- side: 'BUY',
327
- });
328
- console.log(` Order result: ${JSON.stringify(result)}`);
329
- if (result.success) {
330
- console.log(' ✓ Order placed successfully (unexpected with no balance, but signing works!)');
331
- // Cancel it immediately
332
- if (result.orderId) {
333
- await pmClient.cancelOrder(result.orderId);
334
- console.log(' Cancelled test order\n');
335
- }
336
- passed++;
337
- } else {
338
- console.log(' Order rejected (expected — no balance)\n');
339
- // This is still a pass for signing if the error isn't about signatures
340
- passed++;
341
- }
342
- } catch (orderError) {
343
- const msg = orderError.message || String(orderError);
344
- console.log(` Order error: ${msg}`);
345
- if (msg.toLowerCase().includes('signature') || msg.toLowerCase().includes('auth')) {
346
- console.log(' ✗ Order SIGNING failed\n');
347
- failed++;
348
- } else {
349
- console.log(' ✓ Order rejected for non-signing reason (balance, size, etc.)\n');
350
- passed++;
351
- }
352
- }
353
- }
354
- }
355
- } catch (error) {
356
- console.log(` ✗ Error: ${error.message}\n`);
357
- failed++;
358
- }
359
-
360
- // ═══════════════════════════════════════════════════════════
361
- // SUMMARY
362
- // ═══════════════════════════════════════════════════════════
363
-
364
- console.log('═══════════════════════════════════════════════');
365
- console.log(` RESULTS: ${passed} passed, ${failed} failed`);
366
- console.log('═══════════════════════════════════════════════');
367
-
368
- if (failed > 0) {
369
- console.log('\n⚠ Some tests failed. The signing/auth pipeline needs fixes before live trading.');
370
- process.exit(1);
371
- } else {
372
- console.log('\n✓ All signing/auth tests passed. The code is ready for funded live testing.');
373
- process.exit(0);
374
- }