@exagent/agent 0.3.5 → 0.3.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-7UGLJO6W.js +6392 -0
- package/dist/chunk-EHAOPCTJ.js +6406 -0
- package/dist/chunk-FGMXTW5I.js +6540 -0
- package/dist/chunk-IVA2SCSN.js +6756 -0
- package/dist/chunk-JHXCSGPC.js +6352 -0
- package/dist/chunk-V6O4UXVN.js +6345 -0
- package/dist/chunk-ZRAOPQQW.js +6406 -0
- package/dist/cli.js +40 -98
- package/dist/index.d.ts +24 -2
- package/dist/index.js +1 -1
- package/package.json +17 -14
- package/.turbo/turbo-build.log +0 -17
- package/src/bridge/across.ts +0 -240
- package/src/bridge/bridge-manager.ts +0 -87
- package/src/bridge/index.ts +0 -9
- package/src/bridge/types.ts +0 -77
- package/src/chains.ts +0 -105
- package/src/cli.ts +0 -244
- package/src/config.ts +0 -499
- package/src/diagnostics.ts +0 -335
- package/src/index.ts +0 -98
- package/src/llm/anthropic.ts +0 -63
- package/src/llm/base.ts +0 -264
- package/src/llm/deepseek.ts +0 -48
- package/src/llm/google.ts +0 -63
- package/src/llm/groq.ts +0 -48
- package/src/llm/index.ts +0 -42
- package/src/llm/mistral.ts +0 -48
- package/src/llm/ollama.ts +0 -52
- package/src/llm/openai.ts +0 -51
- package/src/llm/together.ts +0 -48
- package/src/llm-providers.ts +0 -100
- package/src/logger.ts +0 -137
- package/src/paper/executor.ts +0 -201
- package/src/paper/index.ts +0 -1
- package/src/perp/client.ts +0 -200
- package/src/perp/index.ts +0 -12
- package/src/perp/msgpack.ts +0 -272
- package/src/perp/orders.ts +0 -234
- package/src/perp/positions.ts +0 -126
- package/src/perp/signer.ts +0 -277
- package/src/perp/types.ts +0 -192
- package/src/perp/websocket.ts +0 -274
- package/src/position-tracker.ts +0 -243
- package/src/prediction/client.ts +0 -281
- package/src/prediction/index.ts +0 -3
- package/src/prediction/order-manager.ts +0 -297
- package/src/prediction/types.ts +0 -151
- package/src/relay.ts +0 -254
- package/src/runtime.ts +0 -1755
- package/src/scrub-secrets.ts +0 -39
- package/src/setup.ts +0 -384
- package/src/signal.ts +0 -212
- package/src/spot/aerodrome.ts +0 -158
- package/src/spot/client.ts +0 -138
- package/src/spot/index.ts +0 -11
- package/src/spot/swap-manager.ts +0 -219
- package/src/spot/types.ts +0 -203
- package/src/spot/uniswap.ts +0 -150
- package/src/store.ts +0 -50
- package/src/strategy/index.ts +0 -2
- package/src/strategy/loader.ts +0 -191
- package/src/strategy/templates.ts +0 -125
- package/src/trading/index.ts +0 -2
- package/src/trading/market.ts +0 -120
- package/src/trading/risk.ts +0 -107
- package/src/ui.ts +0 -75
- package/test-bridge-arb-to-base.mjs +0 -223
- package/test-funded-check.mjs +0 -79
- package/test-funded-phase19.mjs +0 -933
- package/test-hl-deposit-recover.mjs +0 -281
- package/test-hl-withdraw.mjs +0 -372
- package/test-live-signing.mjs +0 -374
- package/test-phase7.mjs +0 -416
- package/test-recover-arb.mjs +0 -206
- package/test-spot-bridge.mjs +0 -248
- package/test-wallet-setup.mjs +0 -126
- package/tsconfig.json +0 -8
|
@@ -1,281 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Phase 19 — Hyperliquid deposit fix + fund recovery.
|
|
3
|
-
*
|
|
4
|
-
* The Bridge2 contract accepts plain USDC transfers (not a function call).
|
|
5
|
-
* Test wallet has ~8 USDC + 0.002 ETH on Arbitrum from previous steps.
|
|
6
|
-
*
|
|
7
|
-
* Steps:
|
|
8
|
-
* 1. Deposit 6 USDC to Hyperliquid via plain ERC20 transfer
|
|
9
|
-
* 2. Verify deposit on Hyperliquid
|
|
10
|
-
* 3. Bridge remaining USDC back from Arbitrum → Base
|
|
11
|
-
* 4. Return Base funds to deployer
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import {
|
|
15
|
-
createPublicClient,
|
|
16
|
-
createWalletClient,
|
|
17
|
-
http,
|
|
18
|
-
formatEther,
|
|
19
|
-
formatUnits,
|
|
20
|
-
parseUnits,
|
|
21
|
-
maxUint256,
|
|
22
|
-
} from 'viem';
|
|
23
|
-
import { privateKeyToAccount } from 'viem/accounts';
|
|
24
|
-
import { base, arbitrum } from 'viem/chains';
|
|
25
|
-
|
|
26
|
-
const DEPLOYER_KEY = '0x0991f4e17be491bb11f4cf1d079db1771fe669e2b0d735f2b3ffc0e32d230ac9';
|
|
27
|
-
const TEST_KEY = '0xb027d931f6c8b4b2681451716981432130806f01ff87001c74412b504b810dbe';
|
|
28
|
-
|
|
29
|
-
const deployer = privateKeyToAccount(DEPLOYER_KEY);
|
|
30
|
-
const testWallet = privateKeyToAccount(TEST_KEY);
|
|
31
|
-
|
|
32
|
-
const basePublic = createPublicClient({ chain: base, transport: http('https://mainnet.base.org') });
|
|
33
|
-
const baseTestWallet = createWalletClient({ account: testWallet, chain: base, transport: http('https://mainnet.base.org') });
|
|
34
|
-
|
|
35
|
-
const arbPublic = createPublicClient({ chain: arbitrum, transport: http('https://arb1.arbitrum.io/rpc') });
|
|
36
|
-
const arbTestWallet = createWalletClient({ account: testWallet, chain: arbitrum, transport: http('https://arb1.arbitrum.io/rpc') });
|
|
37
|
-
|
|
38
|
-
const USDC_BASE = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913';
|
|
39
|
-
const USDC_ARB = '0xaf88d065e77c8cC2239327C5EDb3A432268e5831';
|
|
40
|
-
const HL_BRIDGE_ARB = '0x2Df1c51E09aECF9cacB7bc98cB1742757f163dF7';
|
|
41
|
-
const ACROSS_SPOKE_ARB = '0xe35e9842fceaCA96570B734083f4a58e8F7C5f2A';
|
|
42
|
-
const ACROSS_SPOKE_BASE = '0x09aea4b2242abC8bb4BB78D537A67a245A7bEC64';
|
|
43
|
-
const WETH_ARB = '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1';
|
|
44
|
-
const WETH_BASE = '0x4200000000000000000000000000000000000006';
|
|
45
|
-
|
|
46
|
-
const ERC20_ABI = [
|
|
47
|
-
{ type: 'function', name: 'approve', inputs: [{ name: 'spender', type: 'address' }, { name: 'amount', type: 'uint256' }], outputs: [{ type: 'bool' }], stateMutability: 'nonpayable' },
|
|
48
|
-
{ type: 'function', name: 'transfer', inputs: [{ name: 'to', type: 'address' }, { name: 'amount', type: 'uint256' }], outputs: [{ type: 'bool' }], stateMutability: 'nonpayable' },
|
|
49
|
-
{ type: 'function', name: 'balanceOf', inputs: [{ name: 'account', type: 'address' }], outputs: [{ type: 'uint256' }], stateMutability: 'view' },
|
|
50
|
-
{ type: 'function', name: 'allowance', inputs: [{ name: 'owner', type: 'address' }, { name: 'spender', type: 'address' }], outputs: [{ type: 'uint256' }], stateMutability: 'view' },
|
|
51
|
-
];
|
|
52
|
-
|
|
53
|
-
const ACROSS_SPOKE_ABI = [
|
|
54
|
-
{
|
|
55
|
-
type: 'function', name: 'depositV3',
|
|
56
|
-
inputs: [
|
|
57
|
-
{ name: 'depositor', type: 'address' },
|
|
58
|
-
{ name: 'recipient', type: 'address' },
|
|
59
|
-
{ name: 'inputToken', type: 'address' },
|
|
60
|
-
{ name: 'outputToken', type: 'address' },
|
|
61
|
-
{ name: 'inputAmount', type: 'uint256' },
|
|
62
|
-
{ name: 'outputAmount', type: 'uint256' },
|
|
63
|
-
{ name: 'destinationChainId', type: 'uint256' },
|
|
64
|
-
{ name: 'exclusiveRelayer', type: 'address' },
|
|
65
|
-
{ name: 'quoteTimestamp', type: 'uint32' },
|
|
66
|
-
{ name: 'fillDeadline', type: 'uint32' },
|
|
67
|
-
{ name: 'exclusivityDeadline', type: 'uint32' },
|
|
68
|
-
{ name: 'message', type: 'bytes' },
|
|
69
|
-
],
|
|
70
|
-
outputs: [],
|
|
71
|
-
stateMutability: 'payable',
|
|
72
|
-
},
|
|
73
|
-
];
|
|
74
|
-
|
|
75
|
-
function sleep(ms) { return new Promise(r => setTimeout(r, ms)); }
|
|
76
|
-
|
|
77
|
-
async function waitForTx(client, hash, label) {
|
|
78
|
-
console.log(` ⏳ ${label}: waiting for tx ${hash.slice(0, 10)}...`);
|
|
79
|
-
const receipt = await client.waitForTransactionReceipt({ hash, timeout: 120_000 });
|
|
80
|
-
if (receipt.status === 'reverted') throw new Error(`${label} REVERTED: ${hash}`);
|
|
81
|
-
console.log(` ✅ ${label}: confirmed in block ${receipt.blockNumber} (gas: ${receipt.gasUsed})`);
|
|
82
|
-
return receipt;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// ─── Check balances ────────────────────────────────────────────
|
|
86
|
-
|
|
87
|
-
console.log('\n═══ Current State ═══');
|
|
88
|
-
const arbUsdc = await arbPublic.readContract({ address: USDC_ARB, abi: ERC20_ABI, functionName: 'balanceOf', args: [testWallet.address] });
|
|
89
|
-
const arbEth = await arbPublic.getBalance({ address: testWallet.address });
|
|
90
|
-
console.log(` Test wallet on Arbitrum: ${formatUnits(arbUsdc, 6)} USDC, ${formatEther(arbEth)} ETH`);
|
|
91
|
-
|
|
92
|
-
const baseUsdc = await basePublic.readContract({ address: USDC_BASE, abi: ERC20_ABI, functionName: 'balanceOf', args: [testWallet.address] });
|
|
93
|
-
const baseEth = await basePublic.getBalance({ address: testWallet.address });
|
|
94
|
-
console.log(` Test wallet on Base: ${formatUnits(baseUsdc, 6)} USDC, ${formatEther(baseEth)} ETH`);
|
|
95
|
-
|
|
96
|
-
// ─── Step 1: Deposit to Hyperliquid via plain USDC transfer ───
|
|
97
|
-
|
|
98
|
-
console.log('\n═══ Step 1: Deposit USDC to Hyperliquid (plain transfer) ═══');
|
|
99
|
-
|
|
100
|
-
const depositAmount = parseUnits('6', 6); // $6 USDC
|
|
101
|
-
|
|
102
|
-
if (arbUsdc < depositAmount) {
|
|
103
|
-
console.log(` ❌ Not enough USDC (have ${formatUnits(arbUsdc, 6)}, need 6)`);
|
|
104
|
-
process.exit(1);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
console.log(` Transferring ${formatUnits(depositAmount, 6)} USDC to HL bridge contract...`);
|
|
108
|
-
const depositHash = await arbTestWallet.writeContract({
|
|
109
|
-
address: USDC_ARB,
|
|
110
|
-
abi: ERC20_ABI,
|
|
111
|
-
functionName: 'transfer',
|
|
112
|
-
args: [HL_BRIDGE_ARB, depositAmount],
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
await waitForTx(arbPublic, depositHash, 'HL deposit');
|
|
116
|
-
|
|
117
|
-
// ─── Step 2: Verify on Hyperliquid ────────────────────────────
|
|
118
|
-
|
|
119
|
-
console.log('\n═══ Step 2: Verify Hyperliquid Balance ═══');
|
|
120
|
-
console.log(' Waiting 60s for Hyperliquid to process deposit...');
|
|
121
|
-
|
|
122
|
-
for (let i = 0; i < 12; i++) {
|
|
123
|
-
await sleep(5000);
|
|
124
|
-
try {
|
|
125
|
-
const stateRes = await fetch('https://api.hyperliquid.xyz/info', {
|
|
126
|
-
method: 'POST',
|
|
127
|
-
headers: { 'Content-Type': 'application/json' },
|
|
128
|
-
body: JSON.stringify({ type: 'clearinghouseState', user: testWallet.address }),
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
if (stateRes.ok) {
|
|
132
|
-
const state = await stateRes.json();
|
|
133
|
-
const equity = parseFloat(state.marginSummary?.accountValue ?? '0');
|
|
134
|
-
const withdrawable = parseFloat(state.withdrawable ?? '0');
|
|
135
|
-
|
|
136
|
-
if (equity > 0) {
|
|
137
|
-
console.log(` ✅ Hyperliquid deposit confirmed!`);
|
|
138
|
-
console.log(` Account value: $${equity.toFixed(2)}`);
|
|
139
|
-
console.log(` Withdrawable: $${withdrawable.toFixed(2)}`);
|
|
140
|
-
break;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
} catch {}
|
|
144
|
-
if (i % 3 === 0) console.log(` ... waiting (${(i+1)*5}s)`);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// Final check
|
|
148
|
-
const finalState = await fetch('https://api.hyperliquid.xyz/info', {
|
|
149
|
-
method: 'POST',
|
|
150
|
-
headers: { 'Content-Type': 'application/json' },
|
|
151
|
-
body: JSON.stringify({ type: 'clearinghouseState', user: testWallet.address }),
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
if (finalState.ok) {
|
|
155
|
-
const state = await finalState.json();
|
|
156
|
-
const equity = parseFloat(state.marginSummary?.accountValue ?? '0');
|
|
157
|
-
if (equity > 0) {
|
|
158
|
-
console.log(` ✅ Hyperliquid account funded: $${equity.toFixed(2)}`);
|
|
159
|
-
} else {
|
|
160
|
-
console.log(` ⚠️ Deposit may still be processing (can take up to 1 minute)`);
|
|
161
|
-
console.log(` Check manually: https://api.hyperliquid.xyz/info with clearinghouseState`);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// ─── Step 3: Bridge remaining USDC back from Arbitrum → Base ──
|
|
166
|
-
|
|
167
|
-
console.log('\n═══ Step 3: Bridge remaining USDC Arbitrum → Base ═══');
|
|
168
|
-
|
|
169
|
-
const remainingUsdc = await arbPublic.readContract({ address: USDC_ARB, abi: ERC20_ABI, functionName: 'balanceOf', args: [testWallet.address] });
|
|
170
|
-
console.log(` Remaining USDC on Arbitrum: ${formatUnits(remainingUsdc, 6)}`);
|
|
171
|
-
|
|
172
|
-
if (remainingUsdc > parseUnits('0.5', 6)) {
|
|
173
|
-
// Get fee estimate
|
|
174
|
-
const params = new URLSearchParams({
|
|
175
|
-
inputToken: USDC_ARB,
|
|
176
|
-
outputToken: USDC_BASE,
|
|
177
|
-
originChainId: '42161',
|
|
178
|
-
destinationChainId: '8453',
|
|
179
|
-
amount: remainingUsdc.toString(),
|
|
180
|
-
recipient: testWallet.address,
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
const feeRes = await fetch(`https://app.across.to/api/suggested-fees?${params}`);
|
|
184
|
-
if (feeRes.ok) {
|
|
185
|
-
const feeData = await feeRes.json();
|
|
186
|
-
|
|
187
|
-
if (feeData.isAmountTooLow) {
|
|
188
|
-
console.log(` ⚠️ Amount too low to bridge back. Will leave on Arbitrum.`);
|
|
189
|
-
} else {
|
|
190
|
-
const outputAmount = BigInt(remainingUsdc) - BigInt(feeData.totalRelayFee.total);
|
|
191
|
-
const timestamp = parseInt(feeData.timestamp);
|
|
192
|
-
|
|
193
|
-
console.log(` Bridging ${formatUnits(remainingUsdc, 6)} USDC back to Base (output: ${formatUnits(outputAmount, 6)})...`);
|
|
194
|
-
|
|
195
|
-
// Approve to Across SpokePool on Arbitrum
|
|
196
|
-
const allowance = await arbPublic.readContract({ address: USDC_ARB, abi: ERC20_ABI, functionName: 'allowance', args: [testWallet.address, ACROSS_SPOKE_ARB] });
|
|
197
|
-
if (allowance < remainingUsdc) {
|
|
198
|
-
const appHash = await arbTestWallet.writeContract({ address: USDC_ARB, abi: ERC20_ABI, functionName: 'approve', args: [ACROSS_SPOKE_ARB, maxUint256] });
|
|
199
|
-
await waitForTx(arbPublic, appHash, 'Arb approval');
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
const fillDeadline = Math.floor(Date.now() / 1000) + 21600;
|
|
203
|
-
const bridgeHash = await arbTestWallet.writeContract({
|
|
204
|
-
address: ACROSS_SPOKE_ARB,
|
|
205
|
-
abi: ACROSS_SPOKE_ABI,
|
|
206
|
-
functionName: 'depositV3',
|
|
207
|
-
args: [
|
|
208
|
-
testWallet.address, testWallet.address,
|
|
209
|
-
USDC_ARB, USDC_BASE,
|
|
210
|
-
remainingUsdc, outputAmount,
|
|
211
|
-
8453n, // Base chainId
|
|
212
|
-
'0x0000000000000000000000000000000000000000',
|
|
213
|
-
timestamp, fillDeadline, 0, '0x',
|
|
214
|
-
],
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
await waitForTx(arbPublic, bridgeHash, 'Arb→Base bridge');
|
|
218
|
-
|
|
219
|
-
// Poll for fill
|
|
220
|
-
console.log(' Polling for bridge fill...');
|
|
221
|
-
for (let i = 0; i < 60; i++) {
|
|
222
|
-
await sleep(2000);
|
|
223
|
-
try {
|
|
224
|
-
const statusRes = await fetch(`https://app.across.to/api/deposit/status?depositTxHash=${bridgeHash}&originChainId=42161`);
|
|
225
|
-
if (statusRes.ok) {
|
|
226
|
-
const status = await statusRes.json();
|
|
227
|
-
if (status.status === 'filled') {
|
|
228
|
-
console.log(` ✅ Bridge filled!`);
|
|
229
|
-
break;
|
|
230
|
-
}
|
|
231
|
-
if (i % 5 === 0) console.log(` ... status: ${status.status} (${i*2}s)`);
|
|
232
|
-
}
|
|
233
|
-
} catch {}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
await sleep(3000);
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
} else {
|
|
240
|
-
console.log(' No significant USDC to bridge back.');
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
// ─── Step 4: Return all Base funds to deployer ────────────────
|
|
244
|
-
|
|
245
|
-
console.log('\n═══ Step 4: Return Funds to Deployer ═══');
|
|
246
|
-
|
|
247
|
-
// Return USDC
|
|
248
|
-
const finalBaseUsdc = await basePublic.readContract({ address: USDC_BASE, abi: ERC20_ABI, functionName: 'balanceOf', args: [testWallet.address] });
|
|
249
|
-
if (finalBaseUsdc > 0n) {
|
|
250
|
-
console.log(` Returning ${formatUnits(finalBaseUsdc, 6)} USDC to deployer...`);
|
|
251
|
-
const hash = await baseTestWallet.writeContract({
|
|
252
|
-
address: USDC_BASE, abi: ERC20_ABI, functionName: 'transfer',
|
|
253
|
-
args: [deployer.address, finalBaseUsdc],
|
|
254
|
-
});
|
|
255
|
-
await waitForTx(basePublic, hash, 'Return USDC');
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
// Return ETH (keep gas reserve)
|
|
259
|
-
const finalBaseEth = await basePublic.getBalance({ address: testWallet.address });
|
|
260
|
-
const gasReserve = 100000000000000n; // 0.0001 ETH
|
|
261
|
-
if (finalBaseEth > gasReserve * 3n) {
|
|
262
|
-
const returnAmount = finalBaseEth - gasReserve;
|
|
263
|
-
console.log(` Returning ${formatEther(returnAmount)} ETH to deployer...`);
|
|
264
|
-
const hash = await baseTestWallet.sendTransaction({
|
|
265
|
-
to: deployer.address,
|
|
266
|
-
value: returnAmount,
|
|
267
|
-
});
|
|
268
|
-
await waitForTx(basePublic, hash, 'Return ETH');
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
// ─── Final Balance Report ─────────────────────────────────────
|
|
272
|
-
|
|
273
|
-
console.log('\n═══ Final Deployer Balance ═══');
|
|
274
|
-
const deployerEth = await basePublic.getBalance({ address: deployer.address });
|
|
275
|
-
const deployerUsdc = await basePublic.readContract({ address: USDC_BASE, abi: ERC20_ABI, functionName: 'balanceOf', args: [deployer.address] });
|
|
276
|
-
console.log(` Base ETH: ${formatEther(deployerEth)}`);
|
|
277
|
-
console.log(` Base USDC: ${formatUnits(deployerUsdc, 6)}`);
|
|
278
|
-
|
|
279
|
-
console.log('\n════════════════════════════════════════════════');
|
|
280
|
-
console.log('Hyperliquid deposit + recovery completed.');
|
|
281
|
-
console.log('════════════════════════════════════════════════');
|
package/test-hl-withdraw.mjs
DELETED
|
@@ -1,372 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Phase 19 — Hyperliquid Withdrawal Test
|
|
3
|
-
*
|
|
4
|
-
* Withdraws USDC from Hyperliquid back to Arbitrum using the
|
|
5
|
-
* HyperliquidSignTransaction EIP-712 domain (withdraw3 action).
|
|
6
|
-
*
|
|
7
|
-
* Then bridges USDC from Arbitrum back to Base and returns to deployer.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import {
|
|
11
|
-
createPublicClient,
|
|
12
|
-
createWalletClient,
|
|
13
|
-
http,
|
|
14
|
-
formatEther,
|
|
15
|
-
formatUnits,
|
|
16
|
-
maxUint256,
|
|
17
|
-
} from 'viem';
|
|
18
|
-
import { privateKeyToAccount } from 'viem/accounts';
|
|
19
|
-
import { base, arbitrum } from 'viem/chains';
|
|
20
|
-
|
|
21
|
-
const TEST_KEY = '0xb027d931f6c8b4b2681451716981432130806f01ff87001c74412b504b810dbe';
|
|
22
|
-
const DEPLOYER_KEY = '0x0991f4e17be491bb11f4cf1d079db1771fe669e2b0d735f2b3ffc0e32d230ac9';
|
|
23
|
-
|
|
24
|
-
const testWallet = privateKeyToAccount(TEST_KEY);
|
|
25
|
-
const deployer = privateKeyToAccount(DEPLOYER_KEY);
|
|
26
|
-
|
|
27
|
-
const arbPublic = createPublicClient({ chain: arbitrum, transport: http('https://arb1.arbitrum.io/rpc') });
|
|
28
|
-
const arbTestWalletClient = createWalletClient({ account: testWallet, chain: arbitrum, transport: http('https://arb1.arbitrum.io/rpc') });
|
|
29
|
-
const basePublic = createPublicClient({ chain: base, transport: http('https://mainnet.base.org') });
|
|
30
|
-
const baseTestWalletClient = createWalletClient({ account: testWallet, chain: base, transport: http('https://mainnet.base.org') });
|
|
31
|
-
|
|
32
|
-
const USDC_ARB = '0xaf88d065e77c8cC2239327C5EDb3A432268e5831';
|
|
33
|
-
const USDC_BASE = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913';
|
|
34
|
-
const ACROSS_SPOKE_ARB = '0xe35e9842fceaCA96570B734083f4a58e8F7C5f2A';
|
|
35
|
-
|
|
36
|
-
const ERC20_ABI = [
|
|
37
|
-
{ type: 'function', name: 'balanceOf', inputs: [{ name: 'account', type: 'address' }], outputs: [{ type: 'uint256' }], stateMutability: 'view' },
|
|
38
|
-
{ type: 'function', name: 'approve', inputs: [{ name: 'spender', type: 'address' }, { name: 'amount', type: 'uint256' }], outputs: [{ type: 'bool' }], stateMutability: 'nonpayable' },
|
|
39
|
-
{ type: 'function', name: 'allowance', inputs: [{ name: 'owner', type: 'address' }, { name: 'spender', type: 'address' }], outputs: [{ type: 'uint256' }], stateMutability: 'view' },
|
|
40
|
-
{ type: 'function', name: 'transfer', inputs: [{ name: 'to', type: 'address' }, { name: 'amount', type: 'uint256' }], outputs: [{ type: 'bool' }], stateMutability: 'nonpayable' },
|
|
41
|
-
];
|
|
42
|
-
|
|
43
|
-
const ACROSS_SPOKE_ABI = [
|
|
44
|
-
{
|
|
45
|
-
type: 'function', name: 'depositV3',
|
|
46
|
-
inputs: [
|
|
47
|
-
{ name: 'depositor', type: 'address' }, { name: 'recipient', type: 'address' },
|
|
48
|
-
{ name: 'inputToken', type: 'address' }, { name: 'outputToken', type: 'address' },
|
|
49
|
-
{ name: 'inputAmount', type: 'uint256' }, { name: 'outputAmount', type: 'uint256' },
|
|
50
|
-
{ name: 'destinationChainId', type: 'uint256' },
|
|
51
|
-
{ name: 'exclusiveRelayer', type: 'address' },
|
|
52
|
-
{ name: 'quoteTimestamp', type: 'uint32' }, { name: 'fillDeadline', type: 'uint32' },
|
|
53
|
-
{ name: 'exclusivityDeadline', type: 'uint32' }, { name: 'message', type: 'bytes' },
|
|
54
|
-
],
|
|
55
|
-
outputs: [],
|
|
56
|
-
stateMutability: 'payable',
|
|
57
|
-
},
|
|
58
|
-
];
|
|
59
|
-
|
|
60
|
-
function sleep(ms) { return new Promise(r => setTimeout(r, ms)); }
|
|
61
|
-
|
|
62
|
-
async function waitForTx(client, hash, label) {
|
|
63
|
-
console.log(` ⏳ ${label}: tx ${hash.slice(0, 10)}...`);
|
|
64
|
-
const receipt = await client.waitForTransactionReceipt({ hash, timeout: 120_000 });
|
|
65
|
-
if (receipt.status === 'reverted') throw new Error(`${label} REVERTED: ${hash}`);
|
|
66
|
-
console.log(` ✅ ${label}: confirmed (gas: ${receipt.gasUsed})`);
|
|
67
|
-
return receipt;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// ═══════════════════════════════════════════════════════════
|
|
71
|
-
// Step 1: Check Hyperliquid balance
|
|
72
|
-
// ═══════════════════════════════════════════════════════════
|
|
73
|
-
|
|
74
|
-
console.log('═══ Step 1: Check Hyperliquid Balance ═══');
|
|
75
|
-
|
|
76
|
-
const stateRes = await fetch('https://api.hyperliquid.xyz/info', {
|
|
77
|
-
method: 'POST',
|
|
78
|
-
headers: { 'Content-Type': 'application/json' },
|
|
79
|
-
body: JSON.stringify({ type: 'clearinghouseState', user: testWallet.address }),
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
const state = await stateRes.json();
|
|
83
|
-
const equity = parseFloat(state.marginSummary?.accountValue ?? '0');
|
|
84
|
-
const withdrawable = parseFloat(state.withdrawable ?? '0');
|
|
85
|
-
|
|
86
|
-
console.log(` Account: ${testWallet.address}`);
|
|
87
|
-
console.log(` Equity: $${equity.toFixed(2)}`);
|
|
88
|
-
console.log(` Withdrawable: $${withdrawable.toFixed(2)}`);
|
|
89
|
-
|
|
90
|
-
if (withdrawable <= 0) {
|
|
91
|
-
console.log(' No withdrawable balance. Exiting.');
|
|
92
|
-
process.exit(0);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// ═══════════════════════════════════════════════════════════
|
|
96
|
-
// Step 2: Sign and submit withdraw3 action
|
|
97
|
-
// ═══════════════════════════════════════════════════════════
|
|
98
|
-
|
|
99
|
-
console.log('\n═══ Step 2: Withdraw from Hyperliquid ═══');
|
|
100
|
-
|
|
101
|
-
const withdrawAmount = withdrawable.toString(); // Full withdrawable amount
|
|
102
|
-
const timestamp = Date.now();
|
|
103
|
-
|
|
104
|
-
console.log(` Withdrawing $${withdrawAmount} to ${testWallet.address} on Arbitrum`);
|
|
105
|
-
|
|
106
|
-
// EIP-712 domain for user-signed actions (NOT the phantom agent domain!)
|
|
107
|
-
const USER_SIGNED_DOMAIN = {
|
|
108
|
-
name: 'HyperliquidSignTransaction',
|
|
109
|
-
version: '1',
|
|
110
|
-
chainId: 42161, // Arbitrum One mainnet
|
|
111
|
-
verifyingContract: '0x0000000000000000000000000000000000000000',
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
// EIP-712 types for withdrawal
|
|
115
|
-
const WITHDRAW_TYPES = {
|
|
116
|
-
'HyperliquidTransaction:Withdraw': [
|
|
117
|
-
{ name: 'hyperliquidChain', type: 'string' },
|
|
118
|
-
{ name: 'destination', type: 'string' },
|
|
119
|
-
{ name: 'amount', type: 'string' },
|
|
120
|
-
{ name: 'time', type: 'uint64' },
|
|
121
|
-
],
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
// Sign the withdrawal message
|
|
125
|
-
const withdrawMessage = {
|
|
126
|
-
hyperliquidChain: 'Mainnet',
|
|
127
|
-
destination: testWallet.address,
|
|
128
|
-
amount: withdrawAmount,
|
|
129
|
-
time: BigInt(timestamp),
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
console.log(' Signing withdraw3 action...');
|
|
133
|
-
const signature = await arbTestWalletClient.signTypedData({
|
|
134
|
-
account: testWallet,
|
|
135
|
-
domain: USER_SIGNED_DOMAIN,
|
|
136
|
-
types: WITHDRAW_TYPES,
|
|
137
|
-
primaryType: 'HyperliquidTransaction:Withdraw',
|
|
138
|
-
message: withdrawMessage,
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
console.log(` Signature: ${signature.slice(0, 20)}...${signature.slice(-8)}`);
|
|
142
|
-
|
|
143
|
-
// Parse signature into r, s, v
|
|
144
|
-
const sigR = signature.slice(0, 66);
|
|
145
|
-
const sigS = `0x${signature.slice(66, 130)}`;
|
|
146
|
-
const sigV = parseInt(signature.slice(130, 132), 16);
|
|
147
|
-
|
|
148
|
-
// Submit to Hyperliquid Exchange API
|
|
149
|
-
const action = {
|
|
150
|
-
type: 'withdraw3',
|
|
151
|
-
hyperliquidChain: 'Mainnet',
|
|
152
|
-
signatureChainId: '0xa4b1', // 42161 in hex
|
|
153
|
-
destination: testWallet.address,
|
|
154
|
-
amount: withdrawAmount,
|
|
155
|
-
time: timestamp,
|
|
156
|
-
};
|
|
157
|
-
|
|
158
|
-
console.log(' Submitting to Hyperliquid Exchange API...');
|
|
159
|
-
const resp = await fetch('https://api.hyperliquid.xyz/exchange', {
|
|
160
|
-
method: 'POST',
|
|
161
|
-
headers: { 'Content-Type': 'application/json' },
|
|
162
|
-
body: JSON.stringify({
|
|
163
|
-
action,
|
|
164
|
-
nonce: timestamp,
|
|
165
|
-
signature: { r: sigR, s: sigS, v: sigV },
|
|
166
|
-
vaultAddress: null,
|
|
167
|
-
}),
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
const respText = await resp.text();
|
|
171
|
-
let respJson;
|
|
172
|
-
try { respJson = JSON.parse(respText); } catch { respJson = respText; }
|
|
173
|
-
|
|
174
|
-
console.log(` HTTP Status: ${resp.status}`);
|
|
175
|
-
console.log(` Response: ${JSON.stringify(respJson)}`);
|
|
176
|
-
|
|
177
|
-
const respStr = typeof respJson === 'string' ? respJson : JSON.stringify(respJson);
|
|
178
|
-
|
|
179
|
-
if (respStr.toLowerCase().includes('invalid signature') ||
|
|
180
|
-
respStr.toLowerCase().includes('bad signature')) {
|
|
181
|
-
console.log(' ❌ SIGNATURE REJECTED — withdrawal signing is broken!');
|
|
182
|
-
process.exit(1);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
if (resp.status !== 200) {
|
|
186
|
-
console.log(` ❌ Withdrawal failed: ${respStr}`);
|
|
187
|
-
process.exit(1);
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
console.log(' ✅ Withdrawal submitted successfully!');
|
|
191
|
-
|
|
192
|
-
// ═══════════════════════════════════════════════════════════
|
|
193
|
-
// Step 3: Wait for USDC to arrive on Arbitrum
|
|
194
|
-
// ═══════════════════════════════════════════════════════════
|
|
195
|
-
|
|
196
|
-
console.log('\n═══ Step 3: Wait for USDC on Arbitrum ═══');
|
|
197
|
-
console.log(' Hyperliquid withdrawals can take a few minutes...');
|
|
198
|
-
|
|
199
|
-
const startUsdc = await arbPublic.readContract({ address: USDC_ARB, abi: ERC20_ABI, functionName: 'balanceOf', args: [testWallet.address] });
|
|
200
|
-
console.log(` Current Arb USDC: ${formatUnits(startUsdc, 6)}`);
|
|
201
|
-
|
|
202
|
-
// Poll for balance change (Hyperliquid withdrawals take ~1-5 minutes)
|
|
203
|
-
let finalUsdc = startUsdc;
|
|
204
|
-
for (let i = 0; i < 60; i++) { // up to 5 minutes
|
|
205
|
-
await sleep(5000);
|
|
206
|
-
|
|
207
|
-
const newUsdc = await arbPublic.readContract({ address: USDC_ARB, abi: ERC20_ABI, functionName: 'balanceOf', args: [testWallet.address] });
|
|
208
|
-
if (newUsdc > startUsdc) {
|
|
209
|
-
const received = newUsdc - startUsdc;
|
|
210
|
-
console.log(` ✅ Received ${formatUnits(received, 6)} USDC on Arbitrum!`);
|
|
211
|
-
finalUsdc = newUsdc;
|
|
212
|
-
break;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// Also check HL balance
|
|
216
|
-
if (i % 6 === 0) {
|
|
217
|
-
try {
|
|
218
|
-
const hlRes = await fetch('https://api.hyperliquid.xyz/info', {
|
|
219
|
-
method: 'POST',
|
|
220
|
-
headers: { 'Content-Type': 'application/json' },
|
|
221
|
-
body: JSON.stringify({ type: 'clearinghouseState', user: testWallet.address }),
|
|
222
|
-
});
|
|
223
|
-
const hlState = await hlRes.json();
|
|
224
|
-
const hlEq = parseFloat(hlState.marginSummary?.accountValue ?? '0');
|
|
225
|
-
console.log(` ... HL equity: $${hlEq.toFixed(2)}, Arb USDC: ${formatUnits(newUsdc, 6)} (${(i+1)*5}s)`);
|
|
226
|
-
} catch {
|
|
227
|
-
console.log(` ... waiting (${(i+1)*5}s)`);
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
if (finalUsdc === startUsdc) {
|
|
233
|
-
console.log(' ⚠️ USDC not yet received after 5 minutes.');
|
|
234
|
-
console.log(' Withdrawal may still be processing. Check manually later.');
|
|
235
|
-
console.log(` Arbitrum address: ${testWallet.address}`);
|
|
236
|
-
process.exit(0);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// ═══════════════════════════════════════════════════════════
|
|
240
|
-
// Step 4: Bridge USDC from Arbitrum → Base
|
|
241
|
-
// ═══════════════════════════════════════════════════════════
|
|
242
|
-
|
|
243
|
-
console.log('\n═══ Step 4: Bridge USDC Arb → Base ═══');
|
|
244
|
-
|
|
245
|
-
const arbUsdcBal = await arbPublic.readContract({ address: USDC_ARB, abi: ERC20_ABI, functionName: 'balanceOf', args: [testWallet.address] });
|
|
246
|
-
console.log(` Arbitrum USDC: ${formatUnits(arbUsdcBal, 6)}`);
|
|
247
|
-
|
|
248
|
-
if (arbUsdcBal === 0n) {
|
|
249
|
-
console.log(' No USDC to bridge.');
|
|
250
|
-
process.exit(0);
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
// Get fresh fee quote
|
|
254
|
-
const params = new URLSearchParams({
|
|
255
|
-
inputToken: USDC_ARB,
|
|
256
|
-
outputToken: USDC_BASE,
|
|
257
|
-
originChainId: '42161',
|
|
258
|
-
destinationChainId: '8453',
|
|
259
|
-
amount: arbUsdcBal.toString(),
|
|
260
|
-
recipient: testWallet.address,
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
const feeRes = await fetch(`https://app.across.to/api/suggested-fees?${params}`);
|
|
264
|
-
if (!feeRes.ok) {
|
|
265
|
-
console.log(` Fee API error: ${await feeRes.text()}`);
|
|
266
|
-
console.log(' Funds remain on Arbitrum — manual recovery needed.');
|
|
267
|
-
process.exit(1);
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
const feeData = await feeRes.json();
|
|
271
|
-
|
|
272
|
-
if (feeData.isAmountTooLow) {
|
|
273
|
-
console.log(' Amount too low for Across. Sending USDC directly to deployer on Arb.');
|
|
274
|
-
const h = await arbTestWalletClient.writeContract({
|
|
275
|
-
address: USDC_ARB, abi: ERC20_ABI, functionName: 'transfer',
|
|
276
|
-
args: [deployer.address, arbUsdcBal],
|
|
277
|
-
});
|
|
278
|
-
await waitForTx(arbPublic, h, 'Send to deployer');
|
|
279
|
-
process.exit(0);
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
const outputAmount = BigInt(arbUsdcBal) - BigInt(feeData.totalRelayFee.total);
|
|
283
|
-
const quoteTimestamp = parseInt(feeData.timestamp);
|
|
284
|
-
const fillDeadline = Math.floor(Date.now() / 1000) + 21600;
|
|
285
|
-
|
|
286
|
-
console.log(` Output: ${formatUnits(outputAmount, 6)} USDC on Base`);
|
|
287
|
-
|
|
288
|
-
// Approve Across SpokePool
|
|
289
|
-
const allowance = await arbPublic.readContract({ address: USDC_ARB, abi: ERC20_ABI, functionName: 'allowance', args: [testWallet.address, ACROSS_SPOKE_ARB] });
|
|
290
|
-
if (allowance < arbUsdcBal) {
|
|
291
|
-
const h = await arbTestWalletClient.writeContract({ address: USDC_ARB, abi: ERC20_ABI, functionName: 'approve', args: [ACROSS_SPOKE_ARB, maxUint256] });
|
|
292
|
-
await waitForTx(arbPublic, h, 'Approval');
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
// Submit bridge immediately after getting quote
|
|
296
|
-
const bridgeHash = await arbTestWalletClient.writeContract({
|
|
297
|
-
address: ACROSS_SPOKE_ARB,
|
|
298
|
-
abi: ACROSS_SPOKE_ABI,
|
|
299
|
-
functionName: 'depositV3',
|
|
300
|
-
args: [
|
|
301
|
-
testWallet.address, testWallet.address,
|
|
302
|
-
USDC_ARB, USDC_BASE,
|
|
303
|
-
arbUsdcBal, outputAmount,
|
|
304
|
-
8453n,
|
|
305
|
-
'0x0000000000000000000000000000000000000000',
|
|
306
|
-
quoteTimestamp, fillDeadline, 0, '0x',
|
|
307
|
-
],
|
|
308
|
-
});
|
|
309
|
-
|
|
310
|
-
await waitForTx(arbPublic, bridgeHash, 'Arb→Base bridge');
|
|
311
|
-
|
|
312
|
-
// Poll for fill
|
|
313
|
-
console.log(' Polling for bridge fill...');
|
|
314
|
-
for (let i = 0; i < 60; i++) {
|
|
315
|
-
await sleep(2000);
|
|
316
|
-
try {
|
|
317
|
-
const sRes = await fetch(`https://app.across.to/api/deposit/status?depositTxHash=${bridgeHash}&originChainId=42161`);
|
|
318
|
-
if (sRes.ok) {
|
|
319
|
-
const s = await sRes.json();
|
|
320
|
-
if (s.status === 'filled') { console.log(' ✅ Bridge filled!'); break; }
|
|
321
|
-
if (i % 5 === 0) console.log(` ... ${s.status} (${i*2}s)`);
|
|
322
|
-
}
|
|
323
|
-
} catch {}
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
await sleep(3000);
|
|
327
|
-
|
|
328
|
-
// ═══════════════════════════════════════════════════════════
|
|
329
|
-
// Step 5: Return all funds to deployer
|
|
330
|
-
// ═══════════════════════════════════════════════════════════
|
|
331
|
-
|
|
332
|
-
console.log('\n═══ Step 5: Return Funds to Deployer ═══');
|
|
333
|
-
|
|
334
|
-
// USDC on Base
|
|
335
|
-
const finalBaseUsdc = await basePublic.readContract({ address: USDC_BASE, abi: ERC20_ABI, functionName: 'balanceOf', args: [testWallet.address] });
|
|
336
|
-
if (finalBaseUsdc > 0n) {
|
|
337
|
-
console.log(` Returning ${formatUnits(finalBaseUsdc, 6)} USDC...`);
|
|
338
|
-
const h = await baseTestWalletClient.writeContract({
|
|
339
|
-
address: USDC_BASE, abi: ERC20_ABI, functionName: 'transfer',
|
|
340
|
-
args: [deployer.address, finalBaseUsdc],
|
|
341
|
-
});
|
|
342
|
-
await waitForTx(basePublic, h, 'Return USDC');
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
// ETH on Base
|
|
346
|
-
const finalBaseEth = await basePublic.getBalance({ address: testWallet.address });
|
|
347
|
-
if (finalBaseEth > 200000000000000n) {
|
|
348
|
-
const returnEth = finalBaseEth - 100000000000000n;
|
|
349
|
-
console.log(` Returning ${formatEther(returnEth)} ETH...`);
|
|
350
|
-
const h = await baseTestWalletClient.sendTransaction({ to: deployer.address, value: returnEth });
|
|
351
|
-
await waitForTx(basePublic, h, 'Return ETH');
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
// Final report
|
|
355
|
-
console.log('\n═══ Final Deployer Balance ═══');
|
|
356
|
-
const dEth = await basePublic.getBalance({ address: deployer.address });
|
|
357
|
-
const dUsdc = await basePublic.readContract({ address: USDC_BASE, abi: ERC20_ABI, functionName: 'balanceOf', args: [deployer.address] });
|
|
358
|
-
console.log(` Base: ${formatEther(dEth)} ETH, ${formatUnits(dUsdc, 6)} USDC`);
|
|
359
|
-
|
|
360
|
-
// Check HL is empty
|
|
361
|
-
const hlFinal = await fetch('https://api.hyperliquid.xyz/info', {
|
|
362
|
-
method: 'POST',
|
|
363
|
-
headers: { 'Content-Type': 'application/json' },
|
|
364
|
-
body: JSON.stringify({ type: 'clearinghouseState', user: testWallet.address }),
|
|
365
|
-
});
|
|
366
|
-
const hlFinalState = await hlFinal.json();
|
|
367
|
-
const hlFinalEq = parseFloat(hlFinalState.marginSummary?.accountValue ?? '0');
|
|
368
|
-
console.log(` Hyperliquid: $${hlFinalEq.toFixed(2)}`);
|
|
369
|
-
|
|
370
|
-
console.log('\n════════════════════════════════════════════════');
|
|
371
|
-
console.log('Hyperliquid withdrawal + fund recovery complete!');
|
|
372
|
-
console.log('════════════════════════════════════════════════');
|