logiqical 0.2.0 → 0.3.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/dist/index.js CHANGED
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,18 +17,33 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
 
20
30
  // src/index.ts
21
31
  var index_exports = {};
22
32
  __export(index_exports, {
33
+ AgentWallet: () => AgentWallet,
23
34
  BridgeModule: () => BridgeModule,
35
+ CHAINS: () => CHAINS,
36
+ DefiModule: () => DefiModule,
24
37
  DexModule: () => DexModule,
25
38
  LaunchpadModule: () => LaunchpadModule,
26
- LogiqicalAuthError: () => LogiqicalAuthError,
39
+ Logiqical: () => Logiqical,
27
40
  LogiqicalClient: () => LogiqicalClient,
28
41
  LogiqicalError: () => LogiqicalError,
42
+ MarketModule: () => MarketModule,
29
43
  PerpsModule: () => PerpsModule,
44
+ PolicyEngine: () => PolicyEngine,
45
+ PolicyError: () => PolicyError,
46
+ SignalsModule: () => SignalsModule,
30
47
  SocialModule: () => SocialModule,
31
48
  StakingModule: () => StakingModule,
32
49
  SwapModule: () => SwapModule,
@@ -34,994 +51,2486 @@ __export(index_exports, {
34
51
  });
35
52
  module.exports = __toCommonJS(index_exports);
36
53
 
37
- // src/errors.ts
38
- var LogiqicalError = class extends Error {
39
- constructor(message, statusCode, endpoint) {
40
- super(message);
41
- this.statusCode = statusCode;
42
- this.endpoint = endpoint;
43
- this.name = "LogiqicalError";
54
+ // src/client.ts
55
+ var import_ethers9 = require("ethers");
56
+
57
+ // src/wallet.ts
58
+ var import_ethers = require("ethers");
59
+ var import_fs = require("fs");
60
+ var import_path = require("path");
61
+ var import_os = require("os");
62
+ var CHAINS = {
63
+ avalanche: {
64
+ chainId: 43114,
65
+ name: "Avalanche C-Chain",
66
+ rpcUrl: "https://api.avax.network/ext/bc/C/rpc",
67
+ nativeCurrency: { name: "Avalanche", symbol: "AVAX", decimals: 18 },
68
+ blockExplorer: "https://snowtrace.io"
69
+ },
70
+ fuji: {
71
+ chainId: 43113,
72
+ name: "Avalanche Fuji Testnet",
73
+ rpcUrl: "https://api.avax-test.network/ext/bc/C/rpc",
74
+ nativeCurrency: { name: "Avalanche", symbol: "AVAX", decimals: 18 },
75
+ blockExplorer: "https://testnet.snowtrace.io"
76
+ },
77
+ ethereum: {
78
+ chainId: 1,
79
+ name: "Ethereum",
80
+ rpcUrl: "https://eth.llamarpc.com",
81
+ nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
82
+ blockExplorer: "https://etherscan.io"
83
+ },
84
+ base: {
85
+ chainId: 8453,
86
+ name: "Base",
87
+ rpcUrl: "https://mainnet.base.org",
88
+ nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
89
+ blockExplorer: "https://basescan.org"
90
+ },
91
+ arbitrum: {
92
+ chainId: 42161,
93
+ name: "Arbitrum One",
94
+ rpcUrl: "https://arb1.arbitrum.io/rpc",
95
+ nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
96
+ blockExplorer: "https://arbiscan.io"
97
+ },
98
+ optimism: {
99
+ chainId: 10,
100
+ name: "Optimism",
101
+ rpcUrl: "https://mainnet.optimism.io",
102
+ nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
103
+ blockExplorer: "https://optimistic.etherscan.io"
104
+ },
105
+ polygon: {
106
+ chainId: 137,
107
+ name: "Polygon",
108
+ rpcUrl: "https://polygon-rpc.com",
109
+ nativeCurrency: { name: "MATIC", symbol: "MATIC", decimals: 18 },
110
+ blockExplorer: "https://polygonscan.com"
111
+ },
112
+ bsc: {
113
+ chainId: 56,
114
+ name: "BNB Smart Chain",
115
+ rpcUrl: "https://bsc-dataseed.binance.org",
116
+ nativeCurrency: { name: "BNB", symbol: "BNB", decimals: 18 },
117
+ blockExplorer: "https://bscscan.com"
118
+ },
119
+ fantom: {
120
+ chainId: 250,
121
+ name: "Fantom Opera",
122
+ rpcUrl: "https://rpc.ftm.tools",
123
+ nativeCurrency: { name: "Fantom", symbol: "FTM", decimals: 18 },
124
+ blockExplorer: "https://ftmscan.com"
125
+ },
126
+ gnosis: {
127
+ chainId: 100,
128
+ name: "Gnosis Chain",
129
+ rpcUrl: "https://rpc.gnosischain.com",
130
+ nativeCurrency: { name: "xDAI", symbol: "xDAI", decimals: 18 },
131
+ blockExplorer: "https://gnosisscan.io"
132
+ },
133
+ zksync: {
134
+ chainId: 324,
135
+ name: "zkSync Era",
136
+ rpcUrl: "https://mainnet.era.zksync.io",
137
+ nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
138
+ blockExplorer: "https://explorer.zksync.io"
139
+ },
140
+ linea: {
141
+ chainId: 59144,
142
+ name: "Linea",
143
+ rpcUrl: "https://rpc.linea.build",
144
+ nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
145
+ blockExplorer: "https://lineascan.build"
146
+ },
147
+ scroll: {
148
+ chainId: 534352,
149
+ name: "Scroll",
150
+ rpcUrl: "https://rpc.scroll.io",
151
+ nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
152
+ blockExplorer: "https://scrollscan.com"
153
+ },
154
+ blast: {
155
+ chainId: 81457,
156
+ name: "Blast",
157
+ rpcUrl: "https://rpc.blast.io",
158
+ nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
159
+ blockExplorer: "https://blastscan.io"
160
+ },
161
+ mantle: {
162
+ chainId: 5e3,
163
+ name: "Mantle",
164
+ rpcUrl: "https://rpc.mantle.xyz",
165
+ nativeCurrency: { name: "MNT", symbol: "MNT", decimals: 18 },
166
+ blockExplorer: "https://mantlescan.xyz"
167
+ },
168
+ celo: {
169
+ chainId: 42220,
170
+ name: "Celo",
171
+ rpcUrl: "https://forno.celo.org",
172
+ nativeCurrency: { name: "CELO", symbol: "CELO", decimals: 18 },
173
+ blockExplorer: "https://celoscan.io"
174
+ },
175
+ moonbeam: {
176
+ chainId: 1284,
177
+ name: "Moonbeam",
178
+ rpcUrl: "https://rpc.api.moonbeam.network",
179
+ nativeCurrency: { name: "GLMR", symbol: "GLMR", decimals: 18 },
180
+ blockExplorer: "https://moonscan.io"
181
+ },
182
+ sei: {
183
+ chainId: 1329,
184
+ name: "Sei",
185
+ rpcUrl: "https://evm-rpc.sei-apis.com",
186
+ nativeCurrency: { name: "SEI", symbol: "SEI", decimals: 18 },
187
+ blockExplorer: "https://seitrace.com"
188
+ },
189
+ mode: {
190
+ chainId: 34443,
191
+ name: "Mode",
192
+ rpcUrl: "https://mainnet.mode.network",
193
+ nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
194
+ blockExplorer: "https://explorer.mode.network"
195
+ },
196
+ aurora: {
197
+ chainId: 1313161554,
198
+ name: "Aurora",
199
+ rpcUrl: "https://mainnet.aurora.dev",
200
+ nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
201
+ blockExplorer: "https://explorer.aurora.dev"
44
202
  }
45
203
  };
46
- var LogiqicalAuthError = class extends LogiqicalError {
47
- constructor(endpoint) {
48
- super("Invalid or missing API key. Call client.register() or provide an apiKey in config.", 401, endpoint);
49
- this.name = "LogiqicalAuthError";
204
+ var KEYSTORE_DIR = (0, import_path.join)((0, import_os.homedir)(), ".logiqical", "keys");
205
+ function ensureKeystoreDir() {
206
+ if (!(0, import_fs.existsSync)(KEYSTORE_DIR)) {
207
+ (0, import_fs.mkdirSync)(KEYSTORE_DIR, { recursive: true });
208
+ }
209
+ }
210
+ var AgentWallet = class _AgentWallet {
211
+ wallet;
212
+ provider;
213
+ chain;
214
+ address;
215
+ constructor(wallet, provider, chain) {
216
+ this.wallet = wallet;
217
+ this.provider = provider;
218
+ this.chain = chain;
219
+ this.address = wallet.address;
220
+ }
221
+ /** The private key (use carefully) */
222
+ get privateKey() {
223
+ return this.wallet.privateKey;
224
+ }
225
+ // ── Factory Methods ──
226
+ /** Generate a brand new wallet with a random private key */
227
+ static generate(config = {}) {
228
+ const { provider, chain } = _AgentWallet.resolveProvider(config);
229
+ const hdWallet = import_ethers.Wallet.createRandom();
230
+ const wallet = new import_ethers.Wallet(hdWallet.privateKey, provider);
231
+ return new _AgentWallet(wallet, provider, chain);
232
+ }
233
+ /** Import an existing wallet from a private key */
234
+ static fromPrivateKey(privateKey, config = {}) {
235
+ const { provider, chain } = _AgentWallet.resolveProvider(config);
236
+ const wallet = new import_ethers.Wallet(privateKey, provider);
237
+ return new _AgentWallet(wallet, provider, chain);
238
+ }
239
+ /** Boot from encrypted keystore, or generate + save if none exists */
240
+ static async boot(config = {}) {
241
+ const password = config.password ?? "logiqical-agent";
242
+ const name = config.keystoreName ?? "agent";
243
+ const keystorePath = (0, import_path.join)(KEYSTORE_DIR, `${name}.json`);
244
+ const { provider, chain } = _AgentWallet.resolveProvider(config);
245
+ if ((0, import_fs.existsSync)(keystorePath)) {
246
+ const data = JSON.parse((0, import_fs.readFileSync)(keystorePath, "utf-8"));
247
+ const decrypted = await import_ethers.Wallet.fromEncryptedJson(data.encryptedJson, password);
248
+ const connected = new import_ethers.Wallet(decrypted.privateKey, provider);
249
+ return new _AgentWallet(connected, provider, chain);
250
+ }
251
+ const hdWallet = import_ethers.Wallet.createRandom();
252
+ const wallet = new import_ethers.Wallet(hdWallet.privateKey, provider);
253
+ const agentWallet = new _AgentWallet(wallet, provider, chain);
254
+ await agentWallet.saveKeystore(password, name);
255
+ return agentWallet;
256
+ }
257
+ // ── Keystore Operations ──
258
+ /** Encrypt and save the wallet to the keystore directory */
259
+ async saveKeystore(password, name) {
260
+ const pw = password ?? "logiqical-agent";
261
+ const n = name ?? "agent";
262
+ ensureKeystoreDir();
263
+ const encryptedJson = await this.wallet.encrypt(pw);
264
+ const data = {
265
+ address: this.address,
266
+ encryptedJson,
267
+ network: this.chain.name,
268
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
269
+ };
270
+ const keystorePath = (0, import_path.join)(KEYSTORE_DIR, `${n}.json`);
271
+ (0, import_fs.writeFileSync)(keystorePath, JSON.stringify(data, null, 2));
272
+ return keystorePath;
273
+ }
274
+ // ── Core Operations ──
275
+ /** Get native token balance (formatted) */
276
+ async getBalance() {
277
+ const balance = await this.provider.getBalance(this.address);
278
+ const { formatEther } = await import("ethers");
279
+ return formatEther(balance);
280
+ }
281
+ /** Get balance of any address */
282
+ async getBalanceOf(address) {
283
+ const balance = await this.provider.getBalance(address);
284
+ const { formatEther } = await import("ethers");
285
+ return formatEther(balance);
286
+ }
287
+ /** Sign a message */
288
+ async signMessage(message) {
289
+ return this.wallet.signMessage(message);
290
+ }
291
+ /** Sign typed data (EIP-712) */
292
+ async signTypedData(domain, types, value) {
293
+ return this.wallet.signTypedData(domain, types, value);
294
+ }
295
+ /** Sign a transaction without broadcasting */
296
+ async signTransaction(tx) {
297
+ return this.wallet.signTransaction(tx);
298
+ }
299
+ /** Sign and broadcast a transaction */
300
+ async sendTransaction(tx) {
301
+ return this.wallet.sendTransaction(tx);
302
+ }
303
+ /** Send native token to an address */
304
+ async send(to, amount) {
305
+ const { parseEther } = await import("ethers");
306
+ return this.wallet.sendTransaction({
307
+ to,
308
+ value: parseEther(amount)
309
+ });
310
+ }
311
+ /**
312
+ * Sign and broadcast an unsigned tx object returned by the Logiqical API.
313
+ * Handles the { to, data, value, chainId, gas/gasLimit } format.
314
+ */
315
+ async signAndBroadcast(unsignedTx) {
316
+ const tx = {
317
+ to: unsignedTx.to,
318
+ data: unsignedTx.data,
319
+ value: unsignedTx.value
320
+ };
321
+ if (unsignedTx.gasLimit || unsignedTx.gas) {
322
+ tx.gasLimit = unsignedTx.gasLimit || unsignedTx.gas;
323
+ }
324
+ return this.wallet.sendTransaction(tx);
325
+ }
326
+ /**
327
+ * Sign and broadcast multiple unsigned txs in sequence.
328
+ * Waits for each to confirm before sending the next.
329
+ */
330
+ async signAndBroadcastAll(unsignedTxs, confirmations = 1) {
331
+ const results = [];
332
+ for (const utx of unsignedTxs) {
333
+ const txResponse = await this.signAndBroadcast(utx);
334
+ await txResponse.wait(confirmations);
335
+ results.push(txResponse);
336
+ }
337
+ return results;
338
+ }
339
+ /** Call a read-only contract method (no gas, no signing) */
340
+ async call(tx) {
341
+ return this.provider.call(tx);
342
+ }
343
+ /** Switch to a different network (returns new AgentWallet instance) */
344
+ switchNetwork(network) {
345
+ const { provider, chain } = _AgentWallet.resolveProvider({ network });
346
+ const wallet = new import_ethers.Wallet(this.privateKey, provider);
347
+ return new _AgentWallet(wallet, provider, chain);
348
+ }
349
+ // ── Internal ──
350
+ static resolveProvider(config) {
351
+ const networkKey = config.network ?? "avalanche";
352
+ const chain = CHAINS[networkKey];
353
+ if (!chain && !config.rpcUrl) {
354
+ throw new Error(`Unknown network "${networkKey}". Use a known chain (${Object.keys(CHAINS).join(", ")}) or provide rpcUrl.`);
355
+ }
356
+ const rpcUrl = config.rpcUrl ?? chain.rpcUrl;
357
+ const resolvedChain = chain ?? {
358
+ chainId: 0,
359
+ name: networkKey,
360
+ rpcUrl,
361
+ nativeCurrency: { name: "ETH", symbol: "ETH", decimals: 18 }
362
+ };
363
+ const provider = new import_ethers.JsonRpcProvider(rpcUrl, resolvedChain.chainId || void 0);
364
+ return { provider, chain: resolvedChain };
50
365
  }
51
366
  };
52
367
 
53
- // src/http.ts
54
- var HttpClient = class {
55
- constructor(baseUrl, apiKey) {
56
- this.baseUrl = baseUrl;
57
- this.apiKey = apiKey ?? null;
58
- }
59
- apiKey;
60
- setApiKey(key) {
61
- this.apiKey = key;
62
- }
63
- getApiKey() {
64
- return this.apiKey;
65
- }
66
- async get(path, params, skipAuth) {
67
- const url = new URL(path, this.baseUrl);
68
- const isSkipAuth = typeof skipAuth === "boolean" ? skipAuth : false;
69
- const extraHeaders = typeof skipAuth === "object" ? skipAuth : void 0;
70
- if (params) {
71
- for (const [k, v] of Object.entries(params)) {
72
- if (v !== void 0) url.searchParams.set(k, String(v));
368
+ // src/policy.ts
369
+ var import_ethers2 = require("ethers");
370
+ var PolicyEngine = class {
371
+ policy;
372
+ spendLog = [];
373
+ constructor(policy = {}) {
374
+ this.policy = policy;
375
+ }
376
+ getPolicy() {
377
+ return { ...this.policy };
378
+ }
379
+ setPolicy(policy) {
380
+ this.policy = policy;
381
+ }
382
+ updatePolicy(updates) {
383
+ this.policy = { ...this.policy, ...updates };
384
+ }
385
+ /** Check a transaction against the policy. Throws if rejected. */
386
+ check(tx) {
387
+ const value = tx.value ? BigInt(tx.value) : 0n;
388
+ if (this.policy.maxPerTx) {
389
+ const max = import_ethers2.ethers.parseEther(this.policy.maxPerTx);
390
+ if (value > max) {
391
+ throw new PolicyError(
392
+ `Transaction value ${import_ethers2.ethers.formatEther(value)} exceeds per-tx limit of ${this.policy.maxPerTx}`,
393
+ "MAX_PER_TX_EXCEEDED"
394
+ );
73
395
  }
74
396
  }
75
- const headers = { ...extraHeaders };
76
- if (!isSkipAuth && this.apiKey) headers["X-API-Key"] = this.apiKey;
77
- const res = await fetch(url.toString(), { headers });
78
- return this.handleResponse(res, path);
79
- }
80
- async post(path, body, extraHeaders) {
81
- const url = new URL(path, this.baseUrl);
82
- const headers = { "Content-Type": "application/json", ...extraHeaders };
83
- if (this.apiKey) headers["X-API-Key"] = this.apiKey;
84
- const res = await fetch(url.toString(), { method: "POST", headers, body: JSON.stringify(body) });
85
- return this.handleResponse(res, path);
86
- }
87
- async patch(path, body, extraHeaders) {
88
- const url = new URL(path, this.baseUrl);
89
- const headers = { "Content-Type": "application/json", ...extraHeaders };
90
- if (this.apiKey) headers["X-API-Key"] = this.apiKey;
91
- const res = await fetch(url.toString(), { method: "PATCH", headers, body: JSON.stringify(body) });
92
- return this.handleResponse(res, path);
93
- }
94
- async handleResponse(res, endpoint) {
95
- if (res.status === 401) throw new LogiqicalAuthError(endpoint);
96
- const data = await res.json().catch(() => null);
97
- if (!res.ok) {
98
- const msg = data?.error ?? `HTTP ${res.status}`;
99
- throw new LogiqicalError(msg, res.status, endpoint);
397
+ if (this.policy.allowedContracts && this.policy.allowedContracts.length > 0) {
398
+ const to = tx.to.toLowerCase();
399
+ const allowed = this.policy.allowedContracts.map((a) => a.toLowerCase());
400
+ if (!allowed.includes(to)) {
401
+ throw new PolicyError(
402
+ `Contract ${tx.to} not in allowlist`,
403
+ "CONTRACT_NOT_ALLOWED"
404
+ );
405
+ }
406
+ }
407
+ if (this.policy.blockedContracts && this.policy.blockedContracts.length > 0) {
408
+ const to = tx.to.toLowerCase();
409
+ const blocked = this.policy.blockedContracts.map((b) => b.toLowerCase());
410
+ if (blocked.includes(to)) {
411
+ throw new PolicyError(
412
+ `Contract ${tx.to} is blocked`,
413
+ "CONTRACT_BLOCKED"
414
+ );
415
+ }
416
+ }
417
+ if (this.policy.maxPerHour) {
418
+ const max = import_ethers2.ethers.parseEther(this.policy.maxPerHour);
419
+ const hourSpend = this.getSpendSince(Date.now() - 36e5);
420
+ if (hourSpend + value > max) {
421
+ throw new PolicyError(
422
+ `Would exceed hourly budget of ${this.policy.maxPerHour} AVAX (spent ${import_ethers2.ethers.formatEther(hourSpend)} this hour)`,
423
+ "HOURLY_BUDGET_EXCEEDED"
424
+ );
425
+ }
426
+ }
427
+ if (this.policy.maxPerDay) {
428
+ const max = import_ethers2.ethers.parseEther(this.policy.maxPerDay);
429
+ const daySpend = this.getSpendSince(Date.now() - 864e5);
430
+ if (daySpend + value > max) {
431
+ throw new PolicyError(
432
+ `Would exceed daily budget of ${this.policy.maxPerDay} AVAX (spent ${import_ethers2.ethers.formatEther(daySpend)} today)`,
433
+ "DAILY_BUDGET_EXCEEDED"
434
+ );
435
+ }
100
436
  }
101
- return data;
102
437
  }
103
- };
104
-
105
- // src/modules/swap.ts
106
- var SwapModule = class {
107
- constructor(http, auth) {
108
- this.http = http;
109
- this.auth = auth;
438
+ /** Record a spend after successful broadcast */
439
+ recordSpend(value) {
440
+ this.spendLog.push({ amount: value, timestamp: Date.now() });
441
+ const cutoff = Date.now() - 864e5;
442
+ this.spendLog = this.spendLog.filter((r) => r.timestamp > cutoff);
110
443
  }
111
- /**
112
- * Get AVAX and ARENA token balances for a wallet.
113
- * @param wallet - Wallet address to check
114
- */
115
- async getBalances(wallet) {
116
- await this.auth();
117
- return this.http.get("/balances", { wallet });
444
+ /** Get budget status */
445
+ getBudgetStatus() {
446
+ const hourSpend = this.getSpendSince(Date.now() - 36e5);
447
+ const daySpend = this.getSpendSince(Date.now() - 864e5);
448
+ return {
449
+ spentLastHour: import_ethers2.ethers.formatEther(hourSpend),
450
+ spentLast24h: import_ethers2.ethers.formatEther(daySpend),
451
+ remainingHour: this.policy.maxPerHour ? import_ethers2.ethers.formatEther(import_ethers2.ethers.parseEther(this.policy.maxPerHour) - hourSpend) : null,
452
+ remainingDay: this.policy.maxPerDay ? import_ethers2.ethers.formatEther(import_ethers2.ethers.parseEther(this.policy.maxPerDay) - daySpend) : null
453
+ };
118
454
  }
119
- /**
120
- * Quote how much ARENA you get for a given amount of AVAX.
121
- * @param avax - Amount of AVAX to spend
122
- */
123
- async quote(avax) {
124
- await this.auth();
125
- return this.http.get("/quote", { avax });
455
+ get shouldSimulate() {
456
+ return this.policy.simulateBeforeSend ?? false;
126
457
  }
127
- /**
128
- * Quote how much AVAX you get for selling a given amount of ARENA.
129
- * @param arena - Amount of ARENA to sell
130
- */
131
- async sellQuote(arena) {
132
- await this.auth();
133
- return this.http.get("/quote/sell", { arena });
458
+ get isDryRun() {
459
+ return this.policy.dryRun ?? false;
134
460
  }
135
- /**
136
- * Build unsigned transaction to buy ARENA with AVAX.
137
- *
138
- * Sign the transaction, then broadcast via `client.broadcast()`.
139
- *
140
- * @param wallet - Your wallet address
141
- * @param avax - Amount of AVAX to spend
142
- * @param slippage - Slippage tolerance in basis points (default: 500 = 5%)
143
- */
144
- async buildBuy(wallet, avax, slippage) {
145
- await this.auth();
146
- return this.http.get("/build/buy", { wallet, avax, slippage });
461
+ getSpendSince(since) {
462
+ return this.spendLog.filter((r) => r.timestamp > since).reduce((sum, r) => sum + r.amount, 0n);
147
463
  }
148
- /**
149
- * Build unsigned transactions to sell ARENA for AVAX.
150
- *
151
- * Returns 2 transactions — execute in order:
152
- * 1. Approve — allows the DEX router to spend your ARENA
153
- * 2. Swap — executes the ARENA → AVAX swap
154
- *
155
- * Sign each, broadcast via `client.broadcast()`, wait for confirmation before the next.
156
- *
157
- * @param wallet - Your wallet address
158
- * @param amount - Amount of ARENA to sell, or "max" for entire balance
159
- * @param slippage - Slippage tolerance in basis points (default: 500 = 5%)
160
- */
161
- async buildSell(wallet, amount, slippage) {
162
- await this.auth();
163
- return this.http.get("/build/sell-arena", { wallet, amount, slippage });
464
+ };
465
+ var PolicyError = class extends Error {
466
+ constructor(message, code) {
467
+ super(message);
468
+ this.code = code;
469
+ this.name = "PolicyError";
470
+ }
471
+ };
472
+
473
+ // src/modules/swap.ts
474
+ var import_ethers3 = require("ethers");
475
+
476
+ // src/constants.ts
477
+ var CHAIN_ID = 43114;
478
+ var ARENA_TOKEN = "0xB8d7710f7d8349A506b75dD184F05777c82dAd0C";
479
+ var WAVAX = "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7";
480
+ var ARENA_STAKING = "0xeffb809d99142ce3b51c1796c096f5b01b4aaec4";
481
+ var LB_ROUTER = "0x18556DA13313f3532c54711497A8FedAC273220E";
482
+ var LB_QUOTER = "0x9A550a522BBaDFB69019b0432800Ed17855A51C3";
483
+ var LAUNCH_CONTRACT = "0x8315f1eb449Dd4B779495C3A0b05e5d194446c6e";
484
+ var TOKEN_MANAGER = "0x2196e106af476f57618373ec028924767c758464";
485
+ var AVAX_HELPER = "0x03f1a18519abedbef210fa44e13b71fec01b8dfa";
486
+ var ARENA_PAIRED_THRESHOLD = 100000000000n;
487
+ var GRANULARITY_SCALER = 10n ** 18n;
488
+ var ARENA_SHARES_CONTRACT = "0xc605c2cf66ee98ea925b1bb4fea584b71c00cc4c";
489
+ var FRACTION_SCALER = 100;
490
+ var ARENA_SOCIAL_API = "https://api.starsarena.com";
491
+ var LIFI_API = "https://li.quest/v1";
492
+ var HL_INFO = "https://api.hyperliquid.xyz/info";
493
+ var DEFAULT_SLIPPAGE_BPS = 500;
494
+ var ERC20_ABI = [
495
+ "function balanceOf(address owner) view returns (uint256)",
496
+ "function approve(address spender, uint256 amount) returns (bool)",
497
+ "function allowance(address owner, address spender) view returns (uint256)",
498
+ "function decimals() view returns (uint8)",
499
+ "function symbol() view returns (string)",
500
+ "function name() view returns (string)",
501
+ "function totalSupply() view returns (uint256)"
502
+ ];
503
+ var ARENA_STAKING_ABI = [
504
+ "function deposit(uint256 _amount)",
505
+ "function withdraw(uint256 _amount)",
506
+ "function emergencyWithdraw()",
507
+ "function pendingReward(address _user, address _token) view returns (uint256)",
508
+ "function getUserInfo(address _user, address _rewardToken) view returns (uint256, uint256)",
509
+ "function rewardTokensLength() view returns (uint256)"
510
+ ];
511
+ var LB_ROUTER_ABI = [
512
+ "function swapExactNATIVEForTokens(uint256 amountOutMin, tuple(uint256[] pairBinSteps, uint8[] versions, address[] tokenPath) path, address to, uint256 deadline) payable returns (uint256 amountOut)",
513
+ "function swapExactTokensForNATIVE(uint256 amountIn, uint256 amountOutMin, tuple(uint256[] pairBinSteps, uint8[] versions, address[] tokenPath) path, address payable to, uint256 deadline) returns (uint256 amountOut)"
514
+ ];
515
+ var LB_QUOTER_ABI = [
516
+ "function findBestPathFromAmountIn(address[] calldata route, uint128 amountIn) view returns (tuple(address[] route, address[] pairs, uint256[] binSteps, uint256[] versions, uint128[] amounts, uint128[] virtualAmountsWithoutSlippage, uint128[] fees) quote)"
517
+ ];
518
+ var LAUNCH_CONTRACT_ABI = [
519
+ "function createToken(uint16 a, uint8 b, uint128 curveScaler, uint8 creatorFeeBasisPoints, address tokenCreatorAddress, uint256 tokenSplit, string name, string symbol, uint256 amount) payable",
520
+ "function buyAndCreateLpIfPossible(uint256 amount, uint256 tokenId) payable",
521
+ "function sell(uint256 amount, uint256 tokenId)",
522
+ "function calculateCostWithFees(uint256 amountInToken, uint256 tokenId) view returns (uint256)",
523
+ "function calculateRewardWithFees(uint256 amount, uint256 tokenId) view returns (uint256)",
524
+ "function getMaxTokensForSale(uint256 tokenId) view returns (uint256)",
525
+ "function tokenSupply(uint256 tokenId) view returns (uint256)",
526
+ "function getTokenParameters(uint256 tokenId) view returns (uint128 curveScaler, uint16 a, uint8 b, bool lpDeployed, uint8 lpPercentage, uint8 salePercentage, uint8 creatorFeeBasisPoints, address creatorAddress, address pairAddress, address tokenContractAddress)",
527
+ "function tokenIdentifier() view returns (uint256)",
528
+ "function protocolFeeBasisPoint() view returns (uint256)",
529
+ "event Buy(address indexed user, uint256 indexed tokenId, uint256 tokenAmount, uint256 cost, uint256 tokenSupply, address referrerAddress, uint256 referralFee, uint256 creatorFee, uint256 protocolFee)",
530
+ "event Sell(address indexed user, uint256 indexed tokenId, uint256 tokenAmount, uint256 reward, uint256 tokenSupply, address referrerAddress, uint256 referralFee, uint256 creatorFee, uint256 protocolFee)"
531
+ ];
532
+ var TOKEN_MANAGER_ABI = [
533
+ "function createToken(uint32 a, uint8 b, uint128 curveScaler, uint8 creatorFeeBasisPoints, address tokenCreatorAddress, uint256 tokenSplit, string name, string symbol, uint256 amount)",
534
+ "function calculateCostWithFees(uint256 amountInToken, uint256 tokenId) view returns (uint256)",
535
+ "function calculateRewardWithFees(uint256 amount, uint256 tokenId) view returns (uint256)",
536
+ "function getMaxTokensForSale(uint256 tokenId) view returns (uint256)",
537
+ "function tokenSupply(uint256 tokenId) view returns (uint256)",
538
+ "function getTokenParameters(uint256 tokenId) view returns (uint128 curveScaler, uint32 a, uint8 b, bool lpDeployed, uint8 lpPercentage, uint8 salePercentage, uint8 creatorFeeBasisPoints, address creatorAddress, address pairAddress, address tokenContractAddress)",
539
+ "function tokenIdentifier() view returns (uint256)",
540
+ "function protocolFeeBasisPoint() view returns (uint256)",
541
+ "event Buy(address indexed user, uint256 indexed tokenId, uint256 tokenAmount, uint256 cost, uint256 tokenSupply, address referrerAddress, uint256 referralFee, uint256 creatorFee, uint256 protocolFee)",
542
+ "event Sell(address indexed user, uint256 indexed tokenId, uint256 tokenAmount, uint256 reward, uint256 tokenSupply, address referrerAddress, uint256 referralFee, uint256 creatorFee, uint256 protocolFee)"
543
+ ];
544
+ var AVAX_HELPER_ABI = [
545
+ "function buyAndCreateLpIfPossibleWithAvax(uint256 tokenId, uint256 amountOutMin) payable returns (uint256)",
546
+ "function sellToAvax(uint256 tokenId, uint256 amount, uint256 amountOutAvaxMin) returns (uint256)"
547
+ ];
548
+ var SHARES_ABI = [
549
+ "function buyFractionalShares(address sharesSubject, address user, uint256 amount) payable",
550
+ "function sellFractionalShares(address sharesSubject, address user, uint256 amount) payable",
551
+ "function getBuyPriceForFractionalSharesAfterFee(address sharesSubject, uint256 amount) view returns (uint256)",
552
+ "function getSellPriceForFractionalSharesAfterFee(address sharesSubject, uint256 amount) view returns (uint256)",
553
+ "function getBuyPriceForFractionalShares(address sharesSubject, uint256 amount) view returns (uint256)",
554
+ "function getSellPriceForFractionalShares(address sharesSubject, uint256 amount) view returns (uint256)",
555
+ "function getMyFractionalShares(address sharesSubject, address user) view returns (uint256)",
556
+ "function getSharesSupply(address sharesSubject) view returns (uint256)",
557
+ "function getTotalFractionalSupply(address sharesSubject) view returns (uint256)",
558
+ "function fractionalSharesBalance(address, address) view returns (uint256)",
559
+ "function protocolFeePercent() view returns (uint256)",
560
+ "function subjectFeePercent() view returns (uint256)",
561
+ "function referralFeePercent() view returns (uint256)"
562
+ ];
563
+
564
+ // src/modules/swap.ts
565
+ var SwapModule = class {
566
+ constructor(provider) {
567
+ this.provider = provider;
568
+ this.arenaToken = new import_ethers3.ethers.Contract(import_ethers3.ethers.getAddress(ARENA_TOKEN), ERC20_ABI, provider);
569
+ this.lbQuoter = new import_ethers3.ethers.Contract(import_ethers3.ethers.getAddress(LB_QUOTER), LB_QUOTER_ABI, provider);
570
+ }
571
+ arenaToken;
572
+ lbQuoter;
573
+ /** Get AVAX and ARENA balances for a wallet */
574
+ async getBalances(wallet) {
575
+ const [avax, arena] = await Promise.all([
576
+ this.provider.getBalance(wallet),
577
+ this.arenaToken.balanceOf(wallet)
578
+ ]);
579
+ const decimals = await this.arenaToken.decimals();
580
+ return {
581
+ avax: avax.toString(),
582
+ arena: arena.toString(),
583
+ avaxFormatted: import_ethers3.ethers.formatEther(avax),
584
+ arenaFormatted: import_ethers3.ethers.formatUnits(arena, decimals)
585
+ };
586
+ }
587
+ /** Quote how much ARENA for a given AVAX amount (includes 0.3% fee) */
588
+ async quote(avaxAmount) {
589
+ const amountIn = import_ethers3.ethers.parseEther(avaxAmount);
590
+ const fee = amountIn * 30n / 10000n;
591
+ const netAmount = amountIn - fee;
592
+ const route = [WAVAX, ARENA_TOKEN];
593
+ const quote = await this.lbQuoter.findBestPathFromAmountIn(route, netAmount);
594
+ const arenaOut = quote.amounts[quote.amounts.length - 1];
595
+ const decimals = await this.arenaToken.decimals();
596
+ const arenaFormatted = import_ethers3.ethers.formatUnits(arenaOut, decimals);
597
+ return {
598
+ arenaOut: arenaFormatted,
599
+ fee: import_ethers3.ethers.formatEther(fee),
600
+ netAvax: import_ethers3.ethers.formatEther(netAmount),
601
+ rate: (parseFloat(arenaFormatted) / parseFloat(import_ethers3.ethers.formatEther(netAmount))).toFixed(2)
602
+ };
603
+ }
604
+ /** Quote how much AVAX for selling ARENA */
605
+ async sellQuote(arenaAmount) {
606
+ const decimals = await this.arenaToken.decimals();
607
+ const amountIn = import_ethers3.ethers.parseUnits(arenaAmount, decimals);
608
+ const route = [ARENA_TOKEN, WAVAX];
609
+ const quote = await this.lbQuoter.findBestPathFromAmountIn(route, amountIn);
610
+ const avaxOut = quote.amounts[quote.amounts.length - 1];
611
+ const avaxFormatted = import_ethers3.ethers.formatEther(avaxOut);
612
+ return {
613
+ avaxOut: avaxFormatted,
614
+ arenaIn: arenaAmount,
615
+ rate: (parseFloat(arenaAmount) / parseFloat(avaxFormatted)).toFixed(2)
616
+ };
617
+ }
618
+ /** Build unsigned tx to buy ARENA with AVAX via LFJ DEX */
619
+ async buildBuy(wallet, avaxAmount, slippageBps = DEFAULT_SLIPPAGE_BPS) {
620
+ const amountIn = import_ethers3.ethers.parseEther(avaxAmount);
621
+ const route = [WAVAX, ARENA_TOKEN];
622
+ const quote = await this.lbQuoter.findBestPathFromAmountIn(route, amountIn);
623
+ const expectedOut = quote.amounts[quote.amounts.length - 1];
624
+ if (expectedOut === 0n) throw new Error("Quote returned zero \u2014 pool may have no liquidity");
625
+ const clampedSlippage = BigInt(Math.max(0, Math.min(1e4, Number(slippageBps))));
626
+ const amountOutMin = expectedOut - expectedOut * clampedSlippage / 10000n;
627
+ const deadline = Math.floor(Date.now() / 1e3) + 3600;
628
+ const path = {
629
+ pairBinSteps: [...quote.binSteps].map((b) => b.toString()),
630
+ versions: [...quote.versions].map((v) => Number(v)),
631
+ tokenPath: [...route]
632
+ };
633
+ const iface = new import_ethers3.ethers.Interface(LB_ROUTER_ABI);
634
+ const data = iface.encodeFunctionData("swapExactNATIVEForTokens", [amountOutMin, path, wallet, deadline]);
635
+ const decimals = await this.arenaToken.decimals();
636
+ return {
637
+ transactions: [{
638
+ to: import_ethers3.ethers.getAddress(LB_ROUTER),
639
+ data,
640
+ value: import_ethers3.ethers.toBeHex(amountIn, 32),
641
+ chainId: CHAIN_ID,
642
+ gasLimit: "500000",
643
+ description: `Buy ARENA with ${avaxAmount} AVAX (~${import_ethers3.ethers.formatUnits(expectedOut, decimals)} ARENA)`
644
+ }],
645
+ summary: `Swap ${avaxAmount} AVAX \u2192 ~${import_ethers3.ethers.formatUnits(expectedOut, decimals)} ARENA`
646
+ };
647
+ }
648
+ /** Build unsigned txs to sell ARENA for AVAX: [approve, swap] */
649
+ async buildSell(wallet, arenaAmount, slippageBps = DEFAULT_SLIPPAGE_BPS) {
650
+ const decimals = await this.arenaToken.decimals();
651
+ let sellAmount;
652
+ if (arenaAmount === "max") {
653
+ sellAmount = await this.arenaToken.balanceOf(wallet);
654
+ if (sellAmount === 0n) throw new Error("No ARENA balance to sell");
655
+ } else {
656
+ sellAmount = import_ethers3.ethers.parseUnits(arenaAmount, decimals);
657
+ }
658
+ const route = [ARENA_TOKEN, WAVAX];
659
+ const quote = await this.lbQuoter.findBestPathFromAmountIn(route, sellAmount);
660
+ const expectedOut = quote.amounts[quote.amounts.length - 1];
661
+ if (expectedOut === 0n) throw new Error("Quote returned zero \u2014 pool may have no liquidity");
662
+ const clampedSlippage = BigInt(Math.max(0, Math.min(1e4, Number(slippageBps))));
663
+ const amountOutMin = expectedOut - expectedOut * clampedSlippage / 10000n;
664
+ const deadline = Math.floor(Date.now() / 1e3) + 3600;
665
+ const path = {
666
+ pairBinSteps: [...quote.binSteps].map((b) => b.toString()),
667
+ versions: [...quote.versions].map((v) => Number(v)),
668
+ tokenPath: [...route]
669
+ };
670
+ const approveIface = new import_ethers3.ethers.Interface(ERC20_ABI);
671
+ const approveData = approveIface.encodeFunctionData("approve", [import_ethers3.ethers.getAddress(LB_ROUTER), sellAmount]);
672
+ const swapIface = new import_ethers3.ethers.Interface(LB_ROUTER_ABI);
673
+ const swapData = swapIface.encodeFunctionData("swapExactTokensForNATIVE", [sellAmount, amountOutMin, path, wallet, deadline]);
674
+ return {
675
+ transactions: [
676
+ {
677
+ to: import_ethers3.ethers.getAddress(ARENA_TOKEN),
678
+ data: approveData,
679
+ value: "0",
680
+ chainId: CHAIN_ID,
681
+ gasLimit: "60000",
682
+ description: `Approve ${import_ethers3.ethers.formatUnits(sellAmount, decimals)} ARENA for swap`
683
+ },
684
+ {
685
+ to: import_ethers3.ethers.getAddress(LB_ROUTER),
686
+ data: swapData,
687
+ value: "0",
688
+ chainId: CHAIN_ID,
689
+ gasLimit: "500000",
690
+ description: `Sell ${import_ethers3.ethers.formatUnits(sellAmount, decimals)} ARENA for ~${import_ethers3.ethers.formatEther(expectedOut)} AVAX`
691
+ }
692
+ ]
693
+ };
164
694
  }
165
695
  };
166
696
 
167
697
  // src/modules/staking.ts
698
+ var import_ethers4 = require("ethers");
168
699
  var StakingModule = class {
169
- constructor(http, auth) {
170
- this.http = http;
171
- this.auth = auth;
700
+ constructor(provider) {
701
+ this.provider = provider;
702
+ this.arenaToken = new import_ethers4.ethers.Contract(import_ethers4.ethers.getAddress(ARENA_TOKEN), ERC20_ABI, provider);
703
+ this.staking = new import_ethers4.ethers.Contract(import_ethers4.ethers.getAddress(ARENA_STAKING), ARENA_STAKING_ABI, provider);
172
704
  }
173
- /**
174
- * Get staking info for a wallet — staked amount, pending rewards, APY.
175
- * @param wallet - Wallet address to check
176
- */
705
+ arenaToken;
706
+ staking;
707
+ /** Get staking info staked amount, pending rewards */
177
708
  async getInfo(wallet) {
178
- await this.auth();
179
- return this.http.get("/stake/info", { wallet });
180
- }
181
- /**
182
- * Build unsigned transactions to stake ARENA tokens.
183
- *
184
- * Returns 2 transactions — execute in order:
185
- * 1. Approve — allows the staking contract to spend your ARENA
186
- * 2. Stake — deposits ARENA into staking
187
- *
188
- * @param wallet - Your wallet address
189
- * @param amount - Amount of ARENA to stake
190
- */
709
+ const decimals = await this.arenaToken.decimals();
710
+ const [stakedRaw] = await this.staking.getUserInfo(wallet, import_ethers4.ethers.getAddress(ARENA_TOKEN));
711
+ const pendingRaw = await this.staking.pendingReward(wallet, import_ethers4.ethers.getAddress(ARENA_TOKEN));
712
+ return {
713
+ staked: stakedRaw.toString(),
714
+ stakedFormatted: import_ethers4.ethers.formatUnits(stakedRaw, decimals),
715
+ rewards: pendingRaw.toString(),
716
+ rewardsFormatted: import_ethers4.ethers.formatUnits(pendingRaw, decimals)
717
+ };
718
+ }
719
+ /** Build txs to stake ARENA: [approve, deposit] */
191
720
  async buildStake(wallet, amount) {
192
- await this.auth();
193
- return this.http.get("/build/stake", { wallet, amount });
194
- }
195
- /**
196
- * Build unsigned transactions to buy ARENA with AVAX and stake in one flow.
197
- *
198
- * Combines swap + approve + stake. Returns multiple transactions — execute in order.
199
- *
200
- * @param wallet - Your wallet address
201
- * @param avax - Amount of AVAX to spend
202
- * @param slippage - Slippage tolerance in basis points (default: 500 = 5%)
203
- */
204
- async buildBuyAndStake(wallet, avax, slippage) {
205
- await this.auth();
206
- return this.http.get("/build/buy-and-stake", { wallet, avax, slippage });
207
- }
208
- /**
209
- * Build unsigned transaction to unstake ARENA tokens and claim rewards.
210
- * @param wallet - Your wallet address
211
- * @param amount - Amount of ARENA to unstake
212
- */
721
+ const decimals = await this.arenaToken.decimals();
722
+ let stakeAmount;
723
+ if (amount === "max") {
724
+ stakeAmount = await this.arenaToken.balanceOf(wallet);
725
+ } else {
726
+ stakeAmount = import_ethers4.ethers.parseUnits(amount, decimals);
727
+ }
728
+ const approveIface = new import_ethers4.ethers.Interface(ERC20_ABI);
729
+ const approveData = approveIface.encodeFunctionData("approve", [import_ethers4.ethers.getAddress(ARENA_STAKING), stakeAmount]);
730
+ const stakingIface = new import_ethers4.ethers.Interface(ARENA_STAKING_ABI);
731
+ const stakeData = stakingIface.encodeFunctionData("deposit", [stakeAmount]);
732
+ return {
733
+ transactions: [
734
+ {
735
+ to: import_ethers4.ethers.getAddress(ARENA_TOKEN),
736
+ data: approveData,
737
+ value: "0",
738
+ chainId: CHAIN_ID,
739
+ gasLimit: "60000",
740
+ description: `Approve ${import_ethers4.ethers.formatUnits(stakeAmount, decimals)} ARENA for staking`
741
+ },
742
+ {
743
+ to: import_ethers4.ethers.getAddress(ARENA_STAKING),
744
+ data: stakeData,
745
+ value: "0",
746
+ chainId: CHAIN_ID,
747
+ gasLimit: "300000",
748
+ description: `Stake ${import_ethers4.ethers.formatUnits(stakeAmount, decimals)} ARENA`
749
+ }
750
+ ]
751
+ };
752
+ }
753
+ /** Build tx to unstake ARENA + claim rewards */
213
754
  async buildUnstake(wallet, amount) {
214
- await this.auth();
215
- return this.http.get("/build/unstake", { wallet, amount });
755
+ const decimals = await this.arenaToken.decimals();
756
+ let withdrawAmount;
757
+ if (amount === "max") {
758
+ const [stakedRaw] = await this.staking.getUserInfo(wallet, import_ethers4.ethers.getAddress(ARENA_TOKEN));
759
+ withdrawAmount = stakedRaw;
760
+ } else {
761
+ withdrawAmount = import_ethers4.ethers.parseUnits(amount, decimals);
762
+ }
763
+ const iface = new import_ethers4.ethers.Interface(ARENA_STAKING_ABI);
764
+ const data = iface.encodeFunctionData("withdraw", [withdrawAmount]);
765
+ return {
766
+ transactions: [{
767
+ to: import_ethers4.ethers.getAddress(ARENA_STAKING),
768
+ data,
769
+ value: "0",
770
+ chainId: CHAIN_ID,
771
+ gasLimit: "300000",
772
+ description: `Unstake ${import_ethers4.ethers.formatUnits(withdrawAmount, decimals)} ARENA + claim rewards`
773
+ }]
774
+ };
775
+ }
776
+ /** Build buy-and-stake flow (3 txs): buy ARENA via LFJ, approve, stake */
777
+ async buildBuyAndStake(wallet, avaxAmount, slippageBps = DEFAULT_SLIPPAGE_BPS) {
778
+ const lbQuoter = new import_ethers4.ethers.Contract(import_ethers4.ethers.getAddress(LB_QUOTER), LB_QUOTER_ABI, this.provider);
779
+ const amountIn = import_ethers4.ethers.parseEther(avaxAmount);
780
+ const route = [WAVAX, ARENA_TOKEN];
781
+ const quote = await lbQuoter.findBestPathFromAmountIn(route, amountIn);
782
+ const expectedOut = quote.amounts[quote.amounts.length - 1];
783
+ const clampedSlippage = BigInt(Math.max(0, Math.min(1e4, Number(slippageBps))));
784
+ const amountOutMin = expectedOut - expectedOut * clampedSlippage / 10000n;
785
+ const deadline = Math.floor(Date.now() / 1e3) + 3600;
786
+ const decimals = await this.arenaToken.decimals();
787
+ const path = {
788
+ pairBinSteps: [...quote.binSteps].map((b) => b.toString()),
789
+ versions: [...quote.versions].map((v) => Number(v)),
790
+ tokenPath: [...route]
791
+ };
792
+ const routerIface = new import_ethers4.ethers.Interface(LB_ROUTER_ABI);
793
+ const buyData = routerIface.encodeFunctionData("swapExactNATIVEForTokens", [amountOutMin, path, wallet, deadline]);
794
+ const erc20Iface = new import_ethers4.ethers.Interface(ERC20_ABI);
795
+ const approveData = erc20Iface.encodeFunctionData("approve", [import_ethers4.ethers.getAddress(ARENA_STAKING), import_ethers4.ethers.MaxUint256]);
796
+ const stakingIface = new import_ethers4.ethers.Interface(ARENA_STAKING_ABI);
797
+ const stakeData = stakingIface.encodeFunctionData("deposit", [expectedOut]);
798
+ return {
799
+ transactions: [
800
+ {
801
+ to: import_ethers4.ethers.getAddress(LB_ROUTER),
802
+ data: buyData,
803
+ value: import_ethers4.ethers.toBeHex(amountIn, 32),
804
+ chainId: CHAIN_ID,
805
+ gasLimit: "500000",
806
+ description: `Step 1/3: Buy ~${import_ethers4.ethers.formatUnits(expectedOut, decimals)} ARENA with ${avaxAmount} AVAX`
807
+ },
808
+ {
809
+ to: import_ethers4.ethers.getAddress(ARENA_TOKEN),
810
+ data: approveData,
811
+ value: "0",
812
+ chainId: CHAIN_ID,
813
+ gasLimit: "60000",
814
+ description: `Step 2/3: Approve ARENA for staking`
815
+ },
816
+ {
817
+ to: import_ethers4.ethers.getAddress(ARENA_STAKING),
818
+ data: stakeData,
819
+ value: "0",
820
+ chainId: CHAIN_ID,
821
+ gasLimit: "300000",
822
+ description: `Step 3/3: Stake ~${import_ethers4.ethers.formatUnits(expectedOut, decimals)} ARENA`
823
+ }
824
+ ]
825
+ };
216
826
  }
217
827
  };
218
828
 
219
829
  // src/modules/launchpad.ts
830
+ var import_ethers5 = require("ethers");
220
831
  var LaunchpadModule = class {
221
- constructor(http, auth) {
222
- this.http = http;
223
- this.auth = auth;
224
- }
225
- // ── Discovery ──
226
- /**
227
- * Get recently launched tokens on Arena.
228
- * @param count - Number of tokens (max 50, default 10)
229
- * @param type - Filter: "all", "avax" (AVAX-paired), or "arena" (ARENA-paired)
230
- */
231
- async getRecent(count, type) {
232
- await this.auth();
233
- return this.http.get("/launchpad/recent", { count, type });
234
- }
235
- /**
236
- * Search for a token by name, symbol, or contract address.
237
- * @param q - Search query — name, symbol, or 0x contract address
238
- */
239
- async search(q) {
240
- await this.auth();
241
- return this.http.get("/launchpad/search", { q });
242
- }
243
- /**
244
- * Get tokens that are closest to graduating from the bonding curve to DEX.
245
- * @param count - Number of tokens (max 20, default 5)
246
- */
247
- async getGraduating(count) {
248
- await this.auth();
249
- return this.http.get("/launchpad/graduating", { count });
250
- }
251
- /**
252
- * Get tokens that have already graduated from the bonding curve to DEX.
253
- * @param count - Number of tokens (max 50, default 10)
254
- */
255
- async getGraduated(count) {
256
- await this.auth();
257
- return this.http.get("/launchpad/graduated", { count });
258
- }
259
- /**
260
- * Get top tokens by trading volume.
261
- * @param timeframe - "5m", "1h", "4h", "24h", or "all_time"
262
- * @param count - Number of tokens (max 50, default 10)
263
- */
264
- async getTopVolume(timeframe, count) {
265
- await this.auth();
266
- return this.http.get("/launchpad/top-volume", { timeframe, count });
267
- }
268
- // ── Intelligence ──
269
- /**
270
- * Get full token profile with stats — price, market cap, graduation progress, buy/sell activity.
271
- * @param tokenId - Arena token ID
272
- * @param address - Or token contract address (0x...)
273
- */
274
- async getToken(tokenId, address) {
275
- await this.auth();
276
- return this.http.get("/launchpad/token", { tokenId, address });
277
- }
278
- /**
279
- * Get a buy or sell quote for a bonding curve token.
280
- * @param tokenId - Arena token ID
281
- * @param side - "buy" or "sell"
282
- * @param amount - For buy: AVAX amount. For sell: token amount.
283
- */
284
- async quote(tokenId, side, amount) {
285
- await this.auth();
286
- const params = { tokenId, side };
287
- if (side === "buy") params.avax = amount;
288
- else params.tokenAmount = amount;
289
- return this.http.get("/launchpad/quote", params);
290
- }
291
- /**
292
- * Get agent's tracked portfolio — all launchpad tokens the agent has bought.
293
- * @param wallet - Agent wallet address
294
- */
295
- async getPortfolio(wallet) {
296
- await this.auth();
297
- return this.http.get("/launchpad/portfolio", { wallet });
298
- }
299
- /**
300
- * Get market cap data for a token.
301
- * @param tokenId - Arena token ID
302
- */
303
- async getMarketCap(tokenId) {
304
- await this.auth();
305
- return this.http.get("/launchpad/market-cap", { tokenId });
306
- }
307
- /**
308
- * Get recent trade activity for a token.
309
- * @param tokenId - Arena token ID
310
- * @param address - Or token contract address
311
- * @param count - Number of trades (max 50, default 20)
312
- */
313
- async getActivity(tokenId, address, count) {
314
- await this.auth();
315
- return this.http.get("/launchpad/activity", { tokenId, address, count });
316
- }
317
- /**
318
- * Get top holders for a token with PnL data.
319
- * @param address - Token contract address
320
- * @param tokenId - Or Arena token ID
321
- * @param count - Number of holders (max 50, default 20)
322
- */
323
- async getHolders(address, tokenId, count) {
324
- await this.auth();
325
- return this.http.get("/launchpad/holders", { address, tokenId, count });
326
- }
327
- /**
328
- * Get platform overview — total tokens launched, contract addresses, stats.
329
- */
832
+ constructor(provider) {
833
+ this.provider = provider;
834
+ this.launchContract = new import_ethers5.ethers.Contract(import_ethers5.ethers.getAddress(LAUNCH_CONTRACT), LAUNCH_CONTRACT_ABI, provider);
835
+ this.tokenManager = new import_ethers5.ethers.Contract(import_ethers5.ethers.getAddress(TOKEN_MANAGER), TOKEN_MANAGER_ABI, provider);
836
+ this.avaxHelper = new import_ethers5.ethers.Contract(import_ethers5.ethers.getAddress(AVAX_HELPER), AVAX_HELPER_ABI, provider);
837
+ }
838
+ launchContract;
839
+ tokenManager;
840
+ avaxHelper;
841
+ isArenaPaired(tokenId) {
842
+ return BigInt(tokenId) >= ARENA_PAIRED_THRESHOLD;
843
+ }
844
+ getContract(tokenId) {
845
+ return this.isArenaPaired(tokenId) ? this.tokenManager : this.launchContract;
846
+ }
847
+ /** Get platform overview total tokens, fees, contract addresses */
330
848
  async getOverview() {
331
- await this.auth();
332
- return this.http.get("/launchpad/overview");
333
- }
334
- /**
335
- * Get the global trade feed across all launchpad tokens.
336
- * @param count - Number of trades (max 100, default 50)
337
- * @param offset - Pagination offset
338
- */
339
- async getTrades(count, offset) {
340
- await this.auth();
341
- return this.http.get("/launchpad/trades", { count, offset });
342
- }
343
- // ── Token Launch ──
344
- /**
345
- * Launch a new token on Arena uploads image, creates community, and returns the unsigned createToken transaction.
346
- *
347
- * @param wallet - Creator wallet address
348
- * @param name - Token name (also used as community name)
349
- * @param symbol - Token ticker symbol
350
- * @param imageBase64 - Token image as base64 string (optional)
351
- * @param paymentToken - "avax" or "arena" (default: "arena")
352
- * @param initialBuyAvax - AVAX amount for initial buy at creation (default: "0")
353
- */
354
- async launch(wallet, name, symbol, imageBase64, paymentToken = "arena", initialBuyAvax = "0") {
355
- await this.auth();
356
- return this.http.post("/launchpad/launch", {
357
- wallet,
849
+ const [avaxLatest, arenaLatest, protocolFeeAvax, protocolFeeArena] = await Promise.all([
850
+ this.launchContract.tokenIdentifier(),
851
+ this.tokenManager.tokenIdentifier(),
852
+ this.launchContract.protocolFeeBasisPoint(),
853
+ this.tokenManager.protocolFeeBasisPoint()
854
+ ]);
855
+ return {
856
+ totalAvaxPairedTokens: (avaxLatest - 1n).toString(),
857
+ totalArenaPairedTokens: (arenaLatest - ARENA_PAIRED_THRESHOLD).toString(),
858
+ totalTokens: (avaxLatest - 1n + (arenaLatest - ARENA_PAIRED_THRESHOLD)).toString(),
859
+ protocolFeeBps: { avaxPaired: protocolFeeAvax.toString(), arenaPaired: protocolFeeArena.toString() },
860
+ contracts: { launchContract: LAUNCH_CONTRACT, tokenManager: TOKEN_MANAGER, avaxHelper: AVAX_HELPER }
861
+ };
862
+ }
863
+ /** Get full token info by ID */
864
+ async getToken(tokenId) {
865
+ const contract = this.getContract(tokenId);
866
+ const id = BigInt(tokenId);
867
+ const [params, supply, maxForSale] = await Promise.all([
868
+ contract.getTokenParameters(id),
869
+ contract.tokenSupply(id),
870
+ contract.getMaxTokensForSale(id)
871
+ ]);
872
+ const tokenAddress = params.tokenContractAddress;
873
+ if (tokenAddress === import_ethers5.ethers.ZeroAddress) throw new Error(`Token ID ${tokenId} not found`);
874
+ const token = new import_ethers5.ethers.Contract(tokenAddress, ERC20_ABI, this.provider);
875
+ let name = "Unknown", symbol = "UNKNOWN";
876
+ try {
877
+ [name, symbol] = await Promise.all([token.name(), token.symbol()]);
878
+ } catch {
879
+ }
880
+ let priceAvax = "0";
881
+ if (!params.lpDeployed) {
882
+ try {
883
+ const cost = await contract.calculateCostWithFees(1n, id);
884
+ priceAvax = import_ethers5.ethers.formatEther(cost);
885
+ } catch {
886
+ }
887
+ }
888
+ const salePerc = BigInt(params.salePercentage);
889
+ const saleAllocation = supply * salePerc / 100n;
890
+ const amountSold = saleAllocation > maxForSale ? saleAllocation - maxForSale : 0n;
891
+ const graduationProgress = saleAllocation > 0n ? Number(amountSold * 10000n / saleAllocation) / 100 : 0;
892
+ return {
893
+ tokenId,
894
+ type: this.isArenaPaired(tokenId) ? "ARENA-paired" : "AVAX-paired",
358
895
  name,
359
896
  symbol,
360
- imageBase64,
361
- paymentToken,
362
- initialBuyAvax
363
- });
364
- }
365
- /**
366
- * Upload a token image to Arena's CDN and get the hosted URL.
367
- * Use this before launch() if you want to preview the image URL first.
368
- *
369
- * @param imageBase64 - Image as base64 string
370
- * @param fileType - MIME type (default: "image/jpeg")
371
- */
372
- async uploadImage(imageBase64, fileType = "image/jpeg") {
373
- await this.auth();
374
- return this.http.post("/launchpad/upload-image", { imageBase64, fileType });
375
- }
376
- /**
377
- * Build only the createToken transaction (no image or community creation).
378
- * Useful if you want to handle image upload and community creation separately.
379
- *
380
- * @param wallet - Creator wallet address
381
- * @param name - Token name
382
- * @param symbol - Token ticker symbol
383
- * @param paymentToken - "avax" or "arena" (default: "arena")
384
- * @param initialBuyAvax - AVAX for initial buy (default: "0")
385
- */
386
- async buildCreate(wallet, name, symbol, paymentToken = "arena", initialBuyAvax = "0") {
387
- await this.auth();
388
- return this.http.get("/launchpad/build/create", { wallet, name, symbol, paymentToken, initialBuyAvax });
389
- }
390
- // ── Trading ──
391
- /**
392
- * Build unsigned transaction to buy a launchpad token with AVAX.
393
- *
394
- * Auto-detects if the token is AVAX-paired or ARENA-paired and routes accordingly.
395
- * If the token has graduated to DEX, returns transactions for DEX swap instead.
396
- *
397
- * @param wallet - Your wallet address
398
- * @param tokenId - Arena token ID
399
- * @param avax - Amount of AVAX to spend
400
- * @param slippage - Slippage in basis points (default: 500 = 5%)
401
- */
402
- async buildBuy(wallet, tokenId, avax, slippage) {
403
- await this.auth();
404
- return this.http.get("/launchpad/build/buy", { wallet, tokenId, avax, slippage });
405
- }
406
- /**
407
- * Build unsigned transaction(s) to sell a launchpad token.
408
- *
409
- * Returns approve + sell transactions. Execute in order.
410
- * Use amount="max" to sell entire balance.
411
- *
412
- * If the token has graduated to DEX, returns transactions for DEX swap instead.
413
- *
414
- * @param wallet - Your wallet address
415
- * @param tokenId - Arena token ID
416
- * @param amount - Token amount to sell, or "max" for entire balance
417
- * @param slippage - Slippage in basis points (default: 500 = 5%)
418
- */
419
- async buildSell(wallet, tokenId, amount, slippage) {
420
- await this.auth();
421
- return this.http.get("/launchpad/build/sell", { wallet, tokenId, amount, slippage });
897
+ tokenAddress,
898
+ creator: params.creatorAddress,
899
+ priceAvax,
900
+ graduationProgress: `${graduationProgress.toFixed(2)}%`,
901
+ graduated: params.lpDeployed,
902
+ amountSold: import_ethers5.ethers.formatUnits(amountSold, 18),
903
+ totalSupply: import_ethers5.ethers.formatUnits(supply, 18),
904
+ remainingForSale: import_ethers5.ethers.formatUnits(maxForSale, 18)
905
+ };
906
+ }
907
+ /** Get a buy or sell quote */
908
+ async quote(tokenId, side, amount) {
909
+ const contract = this.getContract(tokenId);
910
+ const id = BigInt(tokenId);
911
+ if (side === "buy") {
912
+ const avaxWei = import_ethers5.ethers.parseEther(amount);
913
+ if (!this.isArenaPaired(tokenId)) {
914
+ const tokenAmountWei2 = await this.binarySearchTokenAmount(avaxWei, id);
915
+ if (tokenAmountWei2 === 0n) return { tokenId, side, avaxIn: amount, tokensOut: "0", note: "Insufficient liquidity" };
916
+ const exactCost = await this.launchContract.calculateCostWithFees(tokenAmountWei2 / GRANULARITY_SCALER, id);
917
+ return { tokenId, side, avaxIn: import_ethers5.ethers.formatEther(exactCost), tokensOut: import_ethers5.ethers.formatUnits(tokenAmountWei2, 18) };
918
+ }
919
+ return { tokenId, side, avaxIn: amount, tokensOut: "determined at execution (ARENA-paired)" };
920
+ }
921
+ const tokenAmountWei = import_ethers5.ethers.parseUnits(amount, 18);
922
+ const reward = await contract.calculateRewardWithFees(tokenAmountWei, id);
923
+ return {
924
+ tokenId,
925
+ side,
926
+ tokenAmount: amount,
927
+ rewardAvax: this.isArenaPaired(tokenId) ? void 0 : import_ethers5.ethers.formatEther(reward),
928
+ rewardArena: this.isArenaPaired(tokenId) ? import_ethers5.ethers.formatUnits(reward, 18) : void 0
929
+ };
930
+ }
931
+ /** Get recent token launches */
932
+ async getRecent(count = 10) {
933
+ const results = [];
934
+ const fetchToken = async (id, contract, pairType) => {
935
+ try {
936
+ const params = await contract.getTokenParameters(id);
937
+ if (params.tokenContractAddress === import_ethers5.ethers.ZeroAddress) return null;
938
+ const token = new import_ethers5.ethers.Contract(params.tokenContractAddress, ERC20_ABI, this.provider);
939
+ let name = "Unknown", symbol = "UNKNOWN";
940
+ try {
941
+ [name, symbol] = await Promise.all([token.name(), token.symbol()]);
942
+ } catch {
943
+ }
944
+ return { tokenId: id.toString(), type: pairType, name, symbol, tokenAddress: params.tokenContractAddress, graduated: params.lpDeployed };
945
+ } catch {
946
+ return null;
947
+ }
948
+ };
949
+ const latestAvax = await this.launchContract.tokenIdentifier();
950
+ const avaxPromises = [];
951
+ for (let i = 0; i < Math.ceil(count / 2) && latestAvax - BigInt(i) > 0n; i++) {
952
+ avaxPromises.push(fetchToken(latestAvax - BigInt(i) - 1n, this.launchContract, "AVAX-paired"));
953
+ }
954
+ const latestArena = await this.tokenManager.tokenIdentifier();
955
+ const arenaPromises = [];
956
+ for (let i = 0; i < Math.ceil(count / 2) && latestArena - BigInt(i) >= ARENA_PAIRED_THRESHOLD; i++) {
957
+ arenaPromises.push(fetchToken(latestArena - BigInt(i) - 1n, this.tokenManager, "ARENA-paired"));
958
+ }
959
+ results.push(...(await Promise.all([...avaxPromises, ...arenaPromises])).filter(Boolean));
960
+ return { count: results.length, tokens: results.slice(0, count) };
961
+ }
962
+ /** Build unsigned tx to buy a launchpad token */
963
+ async buildBuy(wallet, tokenId, avax, slippageBps = DEFAULT_SLIPPAGE_BPS) {
964
+ const id = BigInt(tokenId);
965
+ const avaxWei = import_ethers5.ethers.parseEther(avax);
966
+ const contract = this.getContract(tokenId);
967
+ const params = await contract.getTokenParameters(id);
968
+ if (params.lpDeployed) throw new Error("Token graduated to DEX \u2014 use dex.buildSwap() instead");
969
+ if (!this.isArenaPaired(tokenId)) {
970
+ const tokenAmountWei = await this.binarySearchTokenAmount(avaxWei, id);
971
+ if (tokenAmountWei === 0n) throw new Error("Cannot calculate buy amount \u2014 token may be sold out");
972
+ const iface2 = new import_ethers5.ethers.Interface(LAUNCH_CONTRACT_ABI);
973
+ const data2 = iface2.encodeFunctionData("buyAndCreateLpIfPossible", [tokenAmountWei, id]);
974
+ return { transactions: [{ to: import_ethers5.ethers.getAddress(LAUNCH_CONTRACT), data: data2, value: import_ethers5.ethers.toBeHex(avaxWei, 32), chainId: CHAIN_ID, gasLimit: "500000", description: `Buy ~${import_ethers5.ethers.formatUnits(tokenAmountWei, 18)} tokens (ID ${tokenId})` }] };
975
+ }
976
+ const iface = new import_ethers5.ethers.Interface(AVAX_HELPER_ABI);
977
+ const data = iface.encodeFunctionData("buyAndCreateLpIfPossibleWithAvax", [id, 0n]);
978
+ return { transactions: [{ to: import_ethers5.ethers.getAddress(AVAX_HELPER), data, value: import_ethers5.ethers.toBeHex(avaxWei, 32), chainId: CHAIN_ID, gasLimit: "500000", description: `Buy tokens (ID ${tokenId}) with ${avax} AVAX` }] };
979
+ }
980
+ /** Build unsigned txs to sell a launchpad token: [approve, sell] */
981
+ async buildSell(wallet, tokenId, amount, slippageBps = DEFAULT_SLIPPAGE_BPS) {
982
+ const id = BigInt(tokenId);
983
+ const contract = this.getContract(tokenId);
984
+ const params = await contract.getTokenParameters(id);
985
+ if (params.lpDeployed) throw new Error("Token graduated to DEX \u2014 use dex.buildSwap() instead");
986
+ const tokenAddress = params.tokenContractAddress;
987
+ if (tokenAddress === import_ethers5.ethers.ZeroAddress) throw new Error(`Token ID ${tokenId} not found`);
988
+ const token = new import_ethers5.ethers.Contract(tokenAddress, ERC20_ABI, this.provider);
989
+ let sellAmount;
990
+ if (amount === "max") {
991
+ sellAmount = await token.balanceOf(wallet);
992
+ } else {
993
+ sellAmount = import_ethers5.ethers.parseUnits(amount, 18);
994
+ }
995
+ if (sellAmount === 0n) throw new Error("Zero balance");
996
+ if (!this.isArenaPaired(tokenId)) {
997
+ sellAmount = sellAmount / GRANULARITY_SCALER * GRANULARITY_SCALER;
998
+ if (sellAmount === 0n) throw new Error("Balance too small");
999
+ }
1000
+ const spender = this.isArenaPaired(tokenId) ? import_ethers5.ethers.getAddress(AVAX_HELPER) : import_ethers5.ethers.getAddress(LAUNCH_CONTRACT);
1001
+ const erc20Iface = new import_ethers5.ethers.Interface(ERC20_ABI);
1002
+ const approveTx = { to: tokenAddress, data: erc20Iface.encodeFunctionData("approve", [spender, import_ethers5.ethers.MaxUint256]), value: "0", chainId: CHAIN_ID, gasLimit: "60000", description: "Approve token for selling" };
1003
+ let sellData;
1004
+ let sellTo;
1005
+ if (!this.isArenaPaired(tokenId)) {
1006
+ sellData = new import_ethers5.ethers.Interface(LAUNCH_CONTRACT_ABI).encodeFunctionData("sell", [sellAmount, id]);
1007
+ sellTo = import_ethers5.ethers.getAddress(LAUNCH_CONTRACT);
1008
+ } else {
1009
+ let minOut = 0n;
1010
+ try {
1011
+ const reward = await contract.calculateRewardWithFees(sellAmount, id);
1012
+ minOut = reward - reward * BigInt(slippageBps) / 10000n;
1013
+ } catch {
1014
+ }
1015
+ sellData = new import_ethers5.ethers.Interface(AVAX_HELPER_ABI).encodeFunctionData("sellToAvax", [id, sellAmount, minOut]);
1016
+ sellTo = import_ethers5.ethers.getAddress(AVAX_HELPER);
1017
+ }
1018
+ return { transactions: [approveTx, { to: sellTo, data: sellData, value: "0", chainId: CHAIN_ID, gasLimit: "500000", description: `Sell ${import_ethers5.ethers.formatUnits(sellAmount, 18)} tokens (ID ${tokenId})` }] };
1019
+ }
1020
+ /** Binary search for max tokens purchasable with given AVAX */
1021
+ async binarySearchTokenAmount(avaxBudgetWei, tokenId) {
1022
+ let maxForSale;
1023
+ try {
1024
+ maxForSale = await this.launchContract.getMaxTokensForSale(tokenId);
1025
+ } catch {
1026
+ maxForSale = 100000000n * GRANULARITY_SCALER;
1027
+ }
1028
+ const maxWhole = maxForSale / GRANULARITY_SCALER;
1029
+ if (maxWhole <= 0n) return 0n;
1030
+ let lo = 1n;
1031
+ let hi = maxWhole;
1032
+ let best = 0n;
1033
+ for (let i = 0; i < 30 && lo <= hi; i++) {
1034
+ const mid = (lo + hi) / 2n;
1035
+ try {
1036
+ const cost = await this.launchContract.calculateCostWithFees(mid, tokenId);
1037
+ if (cost <= avaxBudgetWei) {
1038
+ best = mid;
1039
+ lo = mid + 1n;
1040
+ } else {
1041
+ hi = mid - 1n;
1042
+ }
1043
+ } catch {
1044
+ hi = mid - 1n;
1045
+ }
1046
+ }
1047
+ return best > 0n ? best * GRANULARITY_SCALER : 0n;
422
1048
  }
423
1049
  };
424
1050
 
425
1051
  // src/modules/dex.ts
1052
+ var import_ethers6 = require("ethers");
1053
+ var KNOWN_TOKENS = {
1054
+ AVAX: WAVAX,
1055
+ WAVAX,
1056
+ USDC: "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
1057
+ "USDC.e": "0xA7D7079b0FEaD91F3e65f86E8915Cb59c1a4C664",
1058
+ USDT: "0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7",
1059
+ "USDT.e": "0xc7198437980c041c805A1EDcbA50c1Ce5db95118",
1060
+ JOE: "0x6e84a6216eA6dACC71eE8E6b0a5B7322EEbC0fDd",
1061
+ ARENA: "0xB8d7710f7d8349A506b75dD184F05777c82dAd0C",
1062
+ "BTC.b": "0x152b9d0FdC40C096DE20232Db5A0dF62B48dEeEB",
1063
+ "WETH.e": "0x49D5c2BdFfac6CE2BFdB6640F4F80f226bc10bAB",
1064
+ GMX: "0x62edc0692BD897D2295872a9FFCac5425011c661",
1065
+ sAVAX: "0x2b2C81e08f1Af8835a78Bb2A90AE924ACE0eA4bE"
1066
+ };
426
1067
  var DexModule = class {
427
- constructor(http, auth) {
428
- this.http = http;
429
- this.auth = auth;
430
- }
431
- /**
432
- * List all known tokens with addresses and decimals.
433
- * You can also pass any contract address directly to other methods — not limited to this list.
434
- */
435
- async getTokens() {
436
- await this.auth();
437
- return this.http.get("/dex/tokens");
438
- }
439
- /**
440
- * Get on-chain info for any token by contract address — name, symbol, decimals.
441
- * @param address - Token contract address (0x...)
442
- */
1068
+ constructor(provider) {
1069
+ this.provider = provider;
1070
+ this.lbQuoter = new import_ethers6.ethers.Contract(import_ethers6.ethers.getAddress(LB_QUOTER), LB_QUOTER_ABI, provider);
1071
+ }
1072
+ lbQuoter;
1073
+ /** Resolve token symbol or address to a checksummed address */
1074
+ resolveToken(tokenOrAddress) {
1075
+ const upper = tokenOrAddress.toUpperCase();
1076
+ if (KNOWN_TOKENS[upper]) return import_ethers6.ethers.getAddress(KNOWN_TOKENS[upper]);
1077
+ if (KNOWN_TOKENS[tokenOrAddress]) return import_ethers6.ethers.getAddress(KNOWN_TOKENS[tokenOrAddress]);
1078
+ if (tokenOrAddress.startsWith("0x")) return import_ethers6.ethers.getAddress(tokenOrAddress);
1079
+ throw new Error(`Unknown token: ${tokenOrAddress}. Use a symbol (AVAX, USDC, JOE, ARENA) or a 0x contract address.`);
1080
+ }
1081
+ /** List known tokens */
1082
+ getTokens() {
1083
+ const tokens = Object.entries(KNOWN_TOKENS).map(([symbol, address]) => ({ symbol, address }));
1084
+ return { tokens, note: "You can also pass any ERC-20 contract address directly." };
1085
+ }
1086
+ /** Look up any ERC-20 token by address */
443
1087
  async getTokenInfo(address) {
444
- await this.auth();
445
- return this.http.get("/dex/token-info", { address });
1088
+ const addr = import_ethers6.ethers.getAddress(address);
1089
+ const token = new import_ethers6.ethers.Contract(addr, ERC20_ABI, this.provider);
1090
+ const [name, symbol, decimals] = await Promise.all([token.name(), token.symbol(), token.decimals()]);
1091
+ return { address: addr, symbol, name, decimals: Number(decimals) };
1092
+ }
1093
+ /** Get balance of any token */
1094
+ async getBalance(wallet, tokenOrAddress) {
1095
+ const address = this.resolveToken(tokenOrAddress);
1096
+ if (address.toLowerCase() === WAVAX.toLowerCase() && tokenOrAddress.toUpperCase() === "AVAX") {
1097
+ const balance2 = await this.provider.getBalance(wallet);
1098
+ return { wallet, token: "AVAX", balance: balance2.toString(), formatted: import_ethers6.ethers.formatEther(balance2), symbol: "AVAX" };
1099
+ }
1100
+ const token = new import_ethers6.ethers.Contract(address, ERC20_ABI, this.provider);
1101
+ const [balance, symbol, decimals] = await Promise.all([token.balanceOf(wallet), token.symbol(), token.decimals()]);
1102
+ return { wallet, token: address, balance: balance.toString(), formatted: import_ethers6.ethers.formatUnits(balance, decimals), symbol };
446
1103
  }
447
- /**
448
- * Quote a swap between any two tokens on Avalanche.
449
- * @param from - Source token symbol or contract address
450
- * @param to - Destination token symbol or contract address
451
- * @param amount - Amount of source token to swap
452
- */
1104
+ /** Quote a swap between any two tokens */
453
1105
  async quote(from, to, amount) {
454
- await this.auth();
455
- return this.http.get("/dex/quote", { from, to, amount });
456
- }
457
- /**
458
- * Get the balance of any token for a wallet.
459
- * @param wallet - Wallet address
460
- * @param token - Token symbol or contract address
461
- */
462
- async getBalance(wallet, token) {
463
- await this.auth();
464
- return this.http.get("/dex/balance", { wallet, token });
465
- }
466
- /**
467
- * Build unsigned transaction(s) to swap any token pair on Avalanche via LFJ/Pharaoh DEX.
468
- *
469
- * May return multiple transactions (approve + swap). Execute in order.
470
- *
471
- * @param wallet - Your wallet address
472
- * @param from - Source token symbol or contract address
473
- * @param to - Destination token symbol or contract address
474
- * @param amount - Amount to swap, or "max" for entire balance
475
- * @param slippage - Slippage tolerance in basis points (default: 500 = 5%)
476
- */
477
- async buildSwap(wallet, from, to, amount, slippage) {
478
- await this.auth();
479
- return this.http.get("/dex/build/swap", { wallet, from, to, amount, slippage });
1106
+ const fromAddr = this.resolveToken(from);
1107
+ const toAddr = this.resolveToken(to);
1108
+ const fromToken = new import_ethers6.ethers.Contract(fromAddr, ERC20_ABI, this.provider);
1109
+ const decimals = fromAddr.toLowerCase() === WAVAX.toLowerCase() ? 18 : Number(await fromToken.decimals());
1110
+ const amountIn = import_ethers6.ethers.parseUnits(amount, decimals);
1111
+ const route = [fromAddr, toAddr];
1112
+ const quoteResult = await this.lbQuoter.findBestPathFromAmountIn(route, amountIn);
1113
+ const amountOut = quoteResult.amounts[quoteResult.amounts.length - 1];
1114
+ const toToken = new import_ethers6.ethers.Contract(toAddr, ERC20_ABI, this.provider);
1115
+ const toDecimals = toAddr.toLowerCase() === WAVAX.toLowerCase() ? 18 : Number(await toToken.decimals());
1116
+ const outFormatted = import_ethers6.ethers.formatUnits(amountOut, toDecimals);
1117
+ return {
1118
+ from,
1119
+ to,
1120
+ amountIn: amount,
1121
+ amountOut: outFormatted,
1122
+ rate: (parseFloat(outFormatted) / parseFloat(amount)).toFixed(6),
1123
+ path: route
1124
+ };
1125
+ }
1126
+ /** Build swap transactions (1 tx for AVAX→token, 2 txs for token→token with approve) */
1127
+ async buildSwap(wallet, from, to, amount, slippageBps = DEFAULT_SLIPPAGE_BPS) {
1128
+ const fromAddr = this.resolveToken(from);
1129
+ const toAddr = this.resolveToken(to);
1130
+ const isFromNative = from.toUpperCase() === "AVAX";
1131
+ const isToNative = to.toUpperCase() === "AVAX";
1132
+ const fromToken = new import_ethers6.ethers.Contract(fromAddr, ERC20_ABI, this.provider);
1133
+ const fromDecimals = isFromNative ? 18 : Number(await fromToken.decimals());
1134
+ let amountIn;
1135
+ if (amount === "max" && !isFromNative) {
1136
+ amountIn = await fromToken.balanceOf(wallet);
1137
+ if (amountIn === 0n) throw new Error("No balance to swap");
1138
+ } else {
1139
+ amountIn = import_ethers6.ethers.parseUnits(amount, fromDecimals);
1140
+ }
1141
+ const route = [fromAddr, toAddr];
1142
+ const quoteResult = await this.lbQuoter.findBestPathFromAmountIn(route, amountIn);
1143
+ const expectedOut = quoteResult.amounts[quoteResult.amounts.length - 1];
1144
+ if (expectedOut === 0n) throw new Error("Quote returned zero \u2014 no liquidity for this pair");
1145
+ const clampedSlippage = BigInt(Math.max(0, Math.min(1e4, Number(slippageBps))));
1146
+ const amountOutMin = expectedOut - expectedOut * clampedSlippage / 10000n;
1147
+ const deadline = Math.floor(Date.now() / 1e3) + 3600;
1148
+ const path = {
1149
+ pairBinSteps: [...quoteResult.binSteps].map((b) => b.toString()),
1150
+ versions: [...quoteResult.versions].map((v) => Number(v)),
1151
+ tokenPath: [...route]
1152
+ };
1153
+ const txs = [];
1154
+ const routerIface = new import_ethers6.ethers.Interface(LB_ROUTER_ABI);
1155
+ if (isFromNative) {
1156
+ const data = routerIface.encodeFunctionData("swapExactNATIVEForTokens", [amountOutMin, path, wallet, deadline]);
1157
+ txs.push({ to: import_ethers6.ethers.getAddress(LB_ROUTER), data, value: import_ethers6.ethers.toBeHex(amountIn, 32), chainId: CHAIN_ID, gasLimit: "500000", description: `Swap ${amount} AVAX \u2192 ${to}` });
1158
+ } else if (isToNative) {
1159
+ const erc20Iface = new import_ethers6.ethers.Interface(ERC20_ABI);
1160
+ txs.push({
1161
+ to: fromAddr,
1162
+ data: erc20Iface.encodeFunctionData("approve", [import_ethers6.ethers.getAddress(LB_ROUTER), amountIn]),
1163
+ value: "0",
1164
+ chainId: CHAIN_ID,
1165
+ gasLimit: "60000",
1166
+ description: `Approve ${from} for swap`
1167
+ });
1168
+ const data = routerIface.encodeFunctionData("swapExactTokensForNATIVE", [amountIn, amountOutMin, path, wallet, deadline]);
1169
+ txs.push({ to: import_ethers6.ethers.getAddress(LB_ROUTER), data, value: "0", chainId: CHAIN_ID, gasLimit: "500000", description: `Swap ${from} \u2192 AVAX` });
1170
+ } else {
1171
+ const erc20Iface = new import_ethers6.ethers.Interface(ERC20_ABI);
1172
+ txs.push({
1173
+ to: fromAddr,
1174
+ data: erc20Iface.encodeFunctionData("approve", [import_ethers6.ethers.getAddress(LB_ROUTER), amountIn]),
1175
+ value: "0",
1176
+ chainId: CHAIN_ID,
1177
+ gasLimit: "60000",
1178
+ description: `Approve ${from} for swap`
1179
+ });
1180
+ throw new Error("Direct token\u2192token swaps not yet supported. Route through AVAX: swap TOKEN\u2192AVAX, then AVAX\u2192TOKEN.");
1181
+ }
1182
+ const toToken = new import_ethers6.ethers.Contract(toAddr, ERC20_ABI, this.provider);
1183
+ const toDecimals = isToNative ? 18 : Number(await toToken.decimals());
1184
+ return { transactions: txs, summary: `Swap ${amount} ${from} \u2192 ~${import_ethers6.ethers.formatUnits(expectedOut, toDecimals)} ${to}` };
480
1185
  }
481
1186
  };
482
1187
 
483
1188
  // src/modules/perps.ts
484
1189
  var PerpsModule = class {
485
- constructor(http, auth) {
486
- this.http = http;
487
- this.auth = auth;
1190
+ constructor(arenaApiKey) {
1191
+ this.arenaApiKey = arenaApiKey;
488
1192
  }
489
- // ── Setup ──
490
- /**
491
- * Link your Arena API key to enable perps trading.
492
- * One-time setup — after this, all /perp endpoints work automatically.
493
- * @param arenaApiKey - Your Arena API key (from arena.social)
494
- */
495
- async setup(arenaApiKey) {
496
- await this.auth();
497
- return this.http.post("/perp/setup", { arenaApiKey });
1193
+ setArenaApiKey(key) {
1194
+ this.arenaApiKey = key;
498
1195
  }
499
- // ── Registration ──
500
- /** Register for perps trading on Hyperliquid */
1196
+ async arenaRequest(method, path, body, query) {
1197
+ if (!this.arenaApiKey) throw new Error("Arena API key required for perps. Pass arenaApiKey in config.");
1198
+ let url = `${ARENA_SOCIAL_API}${path}`;
1199
+ if (query) {
1200
+ const params = new URLSearchParams(query);
1201
+ url += `?${params.toString()}`;
1202
+ }
1203
+ const headers = { "x-api-key": this.arenaApiKey, "Content-Type": "application/json" };
1204
+ const res = await fetch(url, { method, headers, body: body ? JSON.stringify(body) : void 0 });
1205
+ const data = await res.json();
1206
+ if (!res.ok) {
1207
+ throw new Error(data.message || data.error || `Arena perps error ${res.status}`);
1208
+ }
1209
+ return data;
1210
+ }
1211
+ async hlPost(body) {
1212
+ const res = await fetch(HL_INFO, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body) });
1213
+ if (!res.ok) throw new Error(`Hyperliquid API error ${res.status}`);
1214
+ return res.json();
1215
+ }
1216
+ // ── Setup (via Arena API) ──
501
1217
  async register() {
502
- await this.auth();
503
- return this.http.post("/perp/register", {});
1218
+ return this.arenaRequest("POST", "/agents/perp/register", { provider: "HYPERLIQUID" });
504
1219
  }
505
- /** Check perps registration status */
506
1220
  async getRegistrationStatus() {
507
- await this.auth();
508
- return this.http.get("/perp/registration-status");
1221
+ return this.arenaRequest("GET", "/agents/perp/registration-status", void 0, { provider: "HYPERLIQUID" });
509
1222
  }
510
- /** Get your Hyperliquid API wallet address */
511
1223
  async getWalletAddress() {
512
- await this.auth();
513
- return this.http.get("/perp/wallet-address");
1224
+ return this.arenaRequest("GET", "/agents/perp/wallet-address", void 0, { provider: "HYPERLIQUID" });
514
1225
  }
515
- // ── Auth Flow ──
516
- /** Check which auth steps are completed */
1226
+ // ── Auth Flow (via Arena API — EIP-712 signing) ──
517
1227
  async getAuthStatus() {
518
- await this.auth();
519
- return this.http.post("/perp/auth/status", {});
1228
+ return this.arenaRequest("POST", "/agents/perp/auth/status", { provider: "HYPERLIQUID" });
520
1229
  }
521
- /**
522
- * Get EIP-712 payload for an auth step.
523
- * @param step - Auth step: "accept-terms", "approve-agent", "set-referrer", "approve-builder-fee"
524
- * @param body - Optional body (accept-terms requires mainWalletAddress)
525
- */
526
- async getAuthPayload(step, body) {
527
- await this.auth();
528
- return this.http.post(`/perp/auth/${step}/payload`, body || {});
1230
+ async getAuthPayload(step, mainWalletAddress) {
1231
+ const body = { provider: "HYPERLIQUID" };
1232
+ if (mainWalletAddress) body.mainWalletAddress = mainWalletAddress;
1233
+ return this.arenaRequest("POST", `/agents/perp/auth/${step}/payload`, body);
529
1234
  }
530
- /**
531
- * Submit signed EIP-712 payload for an auth step.
532
- * @param step - Auth step
533
- * @param body - Signed payload with signature and metadata
534
- */
535
- async submitAuthSignature(step, body) {
536
- await this.auth();
537
- return this.http.post(`/perp/auth/${step}/submit`, body);
1235
+ async submitAuthSignature(step, signature, mainWalletAddress, metadata) {
1236
+ return this.arenaRequest("POST", `/agents/perp/auth/${step}/submit`, { provider: "HYPERLIQUID", mainWalletAddress, signature, metadata });
538
1237
  }
539
- /** Enable HIP-3 abstraction (automated, no signature needed) */
540
1238
  async enableHip3() {
541
- await this.auth();
542
- return this.http.post("/perp/auth/enable-hip3", {});
1239
+ return this.arenaRequest("POST", "/agents/perp/auth/enable-hip3", { provider: "HYPERLIQUID" });
543
1240
  }
544
- // ── Market Data ──
545
- /** Get all available perpetual trading pairs (250+ markets) */
1241
+ // ── Market Data (direct Hyperliquid) ──
546
1242
  async getTradingPairs() {
547
- await this.auth();
548
- return this.http.get("/perp/trading-pairs");
549
- }
550
- // ── Leverage ──
551
- /**
552
- * Update leverage for a market. Must be called before first order.
553
- * @param symbol - Market symbol (e.g. "BTC", "ETH")
554
- * @param leverage - Leverage multiplier (1-50 depending on market)
555
- * @param leverageType - "cross" or "isolated"
556
- */
1243
+ const raw = await this.hlPost({ type: "metaAndAssetCtxs" });
1244
+ const universe = raw[0].universe;
1245
+ const pairs = universe.map((m, i) => ({
1246
+ name: m.name,
1247
+ symbol: m.name,
1248
+ baseAssetId: i,
1249
+ sizePrecision: m.szDecimals,
1250
+ maxLeverage: m.maxLeverage,
1251
+ isDelisted: false,
1252
+ marginMode: m.onlyIsolated ? "isolated" : "cross"
1253
+ }));
1254
+ return { pairs, count: pairs.length };
1255
+ }
1256
+ // ── Trading (via Arena API — they hold the signing keys) ──
557
1257
  async updateLeverage(symbol, leverage, leverageType = "cross") {
558
- await this.auth();
559
- return this.http.post("/perp/leverage/update", { symbol, leverage, leverageType });
1258
+ return this.arenaRequest("POST", "/agents/perp/leverage/update", { provider: "HYPERLIQUID", symbol, leverage, leverageType });
560
1259
  }
561
- // ── Trading ──
562
- /**
563
- * Place one or more perpetual orders.
564
- * @param orders - Array of order objects (see docs for BaseOrderParams)
565
- */
566
1260
  async placeOrder(orders) {
567
- await this.auth();
568
- return this.http.post("/perp/orders/place", { orders });
1261
+ return this.arenaRequest("POST", "/agents/perp/orders/place", { provider: "HYPERLIQUID", orders });
569
1262
  }
570
- /**
571
- * Cancel one or more open orders.
572
- * @param cancels - Array of { assetIndex, oid }
573
- */
574
1263
  async cancelOrders(cancels) {
575
- await this.auth();
576
- return this.http.post("/perp/orders/cancel", { cancels });
577
- }
578
- /**
579
- * Modify an existing order.
580
- * @param oid - Order ID to modify
581
- * @param order - New order parameters
582
- */
583
- async modifyOrder(oid, order) {
584
- await this.auth();
585
- return this.http.post("/perp/orders/modify", { oid, order });
1264
+ return this.arenaRequest("POST", "/agents/perp/orders/cancel", { provider: "HYPERLIQUID", cancels });
586
1265
  }
587
- /**
588
- * Close a position (convenience — creates reduce-only IOC with 10% slippage).
589
- * @param symbol - Market symbol
590
- * @param positionSide - "long" or "short"
591
- * @param size - Position size to close
592
- * @param currentPrice - Current market price
593
- * @param closePercent - Percentage to close (default: 100)
594
- */
595
1266
  async closePosition(symbol, positionSide, size, currentPrice, closePercent = 100) {
596
- await this.auth();
597
- return this.http.post("/perp/orders/close-position", {
598
- symbol,
599
- positionSide,
600
- size,
601
- currentPrice,
602
- closePercent
603
- });
1267
+ return this.arenaRequest("POST", "/agents/perp/orders/close-position", { provider: "HYPERLIQUID", symbol, positionSide, size, currentPrice, closePercent });
604
1268
  }
605
- // ── Account Data ──
606
- /** Get open orders */
607
1269
  async getOrders() {
608
- await this.auth();
609
- return this.http.get("/perp/orders");
1270
+ return this.arenaRequest("GET", "/agents/perp/orders", void 0, { provider: "HYPERLIQUID" });
610
1271
  }
611
- /** Get trade execution history */
612
1272
  async getTradeExecutions() {
613
- await this.auth();
614
- return this.http.get("/perp/trade-executions");
1273
+ return this.arenaRequest("GET", "/agents/perp/trade-executions", void 0, { provider: "HYPERLIQUID" });
615
1274
  }
616
- /**
617
- * Get positions and margin summary (queries Hyperliquid directly).
618
- * @param wallet - Your Hyperliquid wallet address (from getWalletAddress)
619
- */
1275
+ // ── Positions (direct Hyperliquid — read-only, no auth needed) ──
620
1276
  async getPositions(wallet) {
621
- await this.auth();
622
- return this.http.get("/perp/positions", { wallet });
1277
+ return this.hlPost({ type: "clearinghouseState", user: wallet });
623
1278
  }
624
- /**
625
- * Get open orders from Hyperliquid directly.
626
- * @param wallet - Your Hyperliquid wallet address
627
- */
628
1279
  async getOpenOrders(wallet) {
629
- await this.auth();
630
- return this.http.get("/perp/open-orders", { wallet });
1280
+ return this.hlPost({ type: "openOrders", user: wallet });
631
1281
  }
632
1282
  };
633
1283
 
634
1284
  // src/modules/bridge.ts
1285
+ var BRIDGE_CHAINS = {
1286
+ ethereum: 1,
1287
+ base: 8453,
1288
+ arbitrum: 42161,
1289
+ optimism: 10,
1290
+ polygon: 137,
1291
+ bsc: 56,
1292
+ avalanche: 43114,
1293
+ fantom: 250,
1294
+ gnosis: 100,
1295
+ zksync: 324,
1296
+ linea: 59144,
1297
+ scroll: 534352,
1298
+ blast: 81457,
1299
+ mantle: 5e3
1300
+ };
1301
+ var USDC = {
1302
+ "1": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
1303
+ "42161": "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
1304
+ "43114": "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
1305
+ "8453": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
1306
+ "10": "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85",
1307
+ "137": "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359"
1308
+ };
1309
+ var NATIVE_TOKEN = "0x0000000000000000000000000000000000000000";
635
1310
  var BridgeModule = class {
636
- constructor(http, auth) {
637
- this.http = http;
638
- this.auth = auth;
639
- }
640
- /**
641
- * Get all supported chains for cross-chain bridging.
642
- */
643
1311
  async getChains() {
644
- await this.auth();
645
- return this.http.get("/bridge/chains");
1312
+ const res = await fetch(`${LIFI_API}/chains`);
1313
+ if (!res.ok) throw new Error(`Li.Fi chains failed (${res.status})`);
1314
+ const data = await res.json();
1315
+ return { chains: data.chains, count: data.chains.length };
646
1316
  }
647
- /**
648
- * Get tokens available on specified chains.
649
- * @param chains - Comma-separated chain IDs (e.g. "43114,42161")
650
- */
651
1317
  async getTokens(chains) {
652
- await this.auth();
653
- return this.http.get("/bridge/tokens", { chains });
1318
+ const res = await fetch(`${LIFI_API}/tokens?chains=${chains}`);
1319
+ if (!res.ok) throw new Error(`Li.Fi tokens failed (${res.status})`);
1320
+ const data = await res.json();
1321
+ return { tokens: data.tokens };
654
1322
  }
655
- /**
656
- * Get info for a specific token on a chain.
657
- * @param chainId - Chain ID
658
- * @param address - Token contract address
659
- */
660
1323
  async getToken(chainId, address) {
661
- await this.auth();
662
- return this.http.get("/bridge/token", { chainId, address });
1324
+ const res = await fetch(`${LIFI_API}/token?chain=${chainId}&token=${address}`);
1325
+ if (!res.ok) throw new Error(`Li.Fi token failed (${res.status})`);
1326
+ return await res.json();
663
1327
  }
664
- /**
665
- * Get available bridge connections between two chains.
666
- * @param fromChainId - Source chain ID
667
- * @param toChainId - Destination chain ID
668
- * @param fromToken - Optional source token address
669
- * @param toToken - Optional destination token address
670
- */
671
1328
  async getConnections(fromChainId, toChainId, fromToken, toToken) {
672
- await this.auth();
673
- const params = { fromChainId, toChainId };
674
- if (fromToken) params.fromToken = fromToken;
675
- if (toToken) params.toToken = toToken;
676
- return this.http.get("/bridge/connections", params);
1329
+ const params = new URLSearchParams({ fromChain: fromChainId.toString(), toChain: toChainId.toString() });
1330
+ if (fromToken) params.set("fromToken", fromToken);
1331
+ if (toToken) params.set("toToken", toToken);
1332
+ const res = await fetch(`${LIFI_API}/connections?${params}`);
1333
+ if (!res.ok) throw new Error(`Li.Fi connections failed (${res.status})`);
1334
+ const data = await res.json();
1335
+ return { connections: data.connections };
677
1336
  }
678
- /**
679
- * Get a bridge quote with unsigned transaction data.
680
- * The agent signs the returned tx on the source chain.
681
- *
682
- * @param fromChainId - Source chain ID
683
- * @param toChainId - Destination chain ID
684
- * @param fromToken - Source token address (use 0x0000...0000 for native tokens)
685
- * @param toToken - Destination token address
686
- * @param fromAmount - Human-readable amount (e.g. "0.1")
687
- * @param fromAddress - Sender wallet address
688
- * @param toAddress - Optional receiver address (defaults to fromAddress)
689
- * @param slippage - Slippage tolerance as decimal (default 0.03 = 3%)
690
- * @param fromDecimals - Token decimals (default 18)
691
- */
692
1337
  async getQuote(fromChainId, toChainId, fromToken, toToken, fromAmount, fromAddress, toAddress, slippage, fromDecimals) {
693
- await this.auth();
694
- const params = { fromChainId, toChainId, fromToken, toToken, fromAmount, fromAddress };
695
- if (toAddress) params.toAddress = toAddress;
696
- if (slippage !== void 0) params.slippage = slippage;
697
- if (fromDecimals !== void 0) params.fromDecimals = fromDecimals;
698
- return this.http.get("/bridge/quote", params);
1338
+ const decimals = fromDecimals ?? 18;
1339
+ const amountWei = this.parseUnits(fromAmount, decimals);
1340
+ const params = new URLSearchParams({
1341
+ fromChain: fromChainId.toString(),
1342
+ toChain: toChainId.toString(),
1343
+ fromToken,
1344
+ toToken,
1345
+ fromAmount: amountWei,
1346
+ fromAddress,
1347
+ toAddress: toAddress ?? fromAddress,
1348
+ slippage: (slippage ?? 0.03).toString(),
1349
+ integrator: "logiqical"
1350
+ });
1351
+ const res = await fetch(`${LIFI_API}/quote?${params}`);
1352
+ if (!res.ok) {
1353
+ const body = await res.text().catch(() => "");
1354
+ throw new Error(`Li.Fi quote failed (${res.status}): ${body}`);
1355
+ }
1356
+ return this.parseQuote(await res.json());
699
1357
  }
700
- /**
701
- * Get multiple bridge route options.
702
- */
703
1358
  async getRoutes(fromChainId, toChainId, fromToken, toToken, fromAmount, fromAddress, toAddress, slippage, fromDecimals) {
704
- await this.auth();
705
- const params = { fromChainId, toChainId, fromToken, toToken, fromAmount, fromAddress };
706
- if (toAddress) params.toAddress = toAddress;
707
- if (slippage !== void 0) params.slippage = slippage;
708
- if (fromDecimals !== void 0) params.fromDecimals = fromDecimals;
709
- return this.http.get("/bridge/routes", params);
1359
+ const decimals = fromDecimals ?? 18;
1360
+ const amountWei = this.parseUnits(fromAmount, decimals);
1361
+ const body = {
1362
+ fromChainId,
1363
+ toChainId,
1364
+ fromTokenAddress: fromToken,
1365
+ toTokenAddress: toToken,
1366
+ fromAmount: amountWei,
1367
+ fromAddress,
1368
+ toAddress: toAddress ?? fromAddress,
1369
+ options: { slippage: slippage ?? 0.03, integrator: "logiqical", order: "RECOMMENDED" }
1370
+ };
1371
+ const res = await fetch(`${LIFI_API}/advanced/routes`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body) });
1372
+ if (!res.ok) {
1373
+ const b = await res.text().catch(() => "");
1374
+ throw new Error(`Li.Fi routes failed (${res.status}): ${b}`);
1375
+ }
1376
+ const data = await res.json();
1377
+ const routes = (data.routes ?? []).map((r) => this.parseRoute(r));
1378
+ return { routes, count: routes.length };
710
1379
  }
711
- /**
712
- * Check bridge transfer status.
713
- * @param txHash - Source chain transaction hash
714
- * @param fromChainId - Source chain ID
715
- * @param toChainId - Destination chain ID
716
- * @param bridge - Optional bridge tool name
717
- */
718
1380
  async getStatus(txHash, fromChainId, toChainId, bridge) {
719
- await this.auth();
720
- const params = { txHash, fromChainId, toChainId };
721
- if (bridge) params.bridge = bridge;
722
- return this.http.get("/bridge/status", params);
723
- }
724
- /**
725
- * Get reference info — common chain IDs, USDC addresses, native token constant.
726
- */
727
- async getInfo() {
728
- await this.auth();
729
- return this.http.get("/bridge/info");
1381
+ const params = new URLSearchParams({ txHash, fromChain: fromChainId.toString(), toChain: toChainId.toString() });
1382
+ if (bridge) params.set("bridge", bridge);
1383
+ const res = await fetch(`${LIFI_API}/status?${params}`);
1384
+ if (!res.ok) throw new Error(`Li.Fi status failed (${res.status})`);
1385
+ return await res.json();
1386
+ }
1387
+ getInfo() {
1388
+ return { chains: BRIDGE_CHAINS, usdc: USDC, nativeToken: NATIVE_TOKEN, tip: "Use 0x0000...0000 for native tokens" };
1389
+ }
1390
+ parseUnits(amount, decimals) {
1391
+ const parts = amount.split(".");
1392
+ const whole = parts[0] || "0";
1393
+ const frac = (parts[1] || "").slice(0, decimals).padEnd(decimals, "0");
1394
+ return (BigInt(whole) * BigInt(10) ** BigInt(decimals) + BigInt(frac)).toString();
1395
+ }
1396
+ parseQuote(data) {
1397
+ const action = data.action ?? {};
1398
+ const estimate = data.estimate ?? {};
1399
+ const txReq = data.transactionRequest;
1400
+ const quote = {
1401
+ id: data.id ?? data.tool ?? "unknown",
1402
+ fromChainId: action.fromChainId ?? 0,
1403
+ toChainId: action.toChainId ?? 0,
1404
+ fromToken: action.fromToken?.address ?? "",
1405
+ toToken: action.toToken?.address ?? "",
1406
+ fromAmount: action.fromAmount ?? "0",
1407
+ toAmount: estimate.toAmount ?? "0",
1408
+ estimatedGas: estimate.gasCosts?.[0]?.amountUSD ?? "0",
1409
+ estimatedTime: estimate.executionDuration ?? 0,
1410
+ tool: data.tool ?? "unknown"
1411
+ };
1412
+ if (txReq) {
1413
+ quote.transaction = { to: txReq.to, data: txReq.data, value: txReq.value ?? "0x0", gasLimit: txReq.gasLimit, chainId: txReq.chainId ?? action.fromChainId };
1414
+ }
1415
+ return quote;
1416
+ }
1417
+ parseRoute(route) {
1418
+ const steps = route.steps ?? [];
1419
+ const firstStep = steps[0] ?? {};
1420
+ const action = firstStep.action ?? {};
1421
+ const estimate = firstStep.estimate ?? {};
1422
+ return {
1423
+ id: route.id ?? "unknown",
1424
+ fromChainId: route.fromChainId ?? 0,
1425
+ toChainId: route.toChainId ?? 0,
1426
+ fromToken: route.fromToken?.address ?? "",
1427
+ toToken: route.toToken?.address ?? "",
1428
+ fromAmount: route.fromAmount ?? "0",
1429
+ toAmount: route.toAmount ?? "0",
1430
+ estimatedGas: route.gasCostUSD ?? "0",
1431
+ estimatedTime: steps.reduce((t, s) => t + (s.estimate?.executionDuration ?? 0), 0),
1432
+ tool: firstStep.tool ?? "unknown"
1433
+ };
730
1434
  }
731
1435
  };
732
1436
 
733
1437
  // src/modules/tickets.ts
1438
+ var import_ethers7 = require("ethers");
734
1439
  var TicketsModule = class {
735
- constructor(http, auth) {
736
- this.http = http;
737
- this.auth = auth;
1440
+ constructor(provider) {
1441
+ this.provider = provider;
1442
+ this.contract = new import_ethers7.ethers.Contract(import_ethers7.ethers.getAddress(ARENA_SHARES_CONTRACT), SHARES_ABI, provider);
738
1443
  }
739
- /** Get buy price for tickets (supports fractional: "0.5", "1.2", "3") */
1444
+ contract;
740
1445
  async getBuyPrice(subject, amount = "1") {
741
- await this.auth();
742
- return this.http.get(`/tickets/price/buy?subject=${subject}&amount=${amount}`);
1446
+ const fractionalAmount = this.ticketsToFractional(amount);
1447
+ const subjectAddr = import_ethers7.ethers.getAddress(subject);
1448
+ const [price, priceWithFee] = await Promise.all([
1449
+ this.contract.getBuyPriceForFractionalShares(subjectAddr, fractionalAmount),
1450
+ this.contract.getBuyPriceForFractionalSharesAfterFee(subjectAddr, fractionalAmount)
1451
+ ]);
1452
+ return { price: import_ethers7.ethers.formatEther(price), priceWithFee: import_ethers7.ethers.formatEther(priceWithFee), tickets: amount, fractionalAmount };
743
1453
  }
744
- /** Get sell price for tickets */
745
1454
  async getSellPrice(subject, amount = "1") {
746
- await this.auth();
747
- return this.http.get(`/tickets/price/sell?subject=${subject}&amount=${amount}`);
1455
+ const fractionalAmount = this.ticketsToFractional(amount);
1456
+ const subjectAddr = import_ethers7.ethers.getAddress(subject);
1457
+ const [price, priceAfterFee] = await Promise.all([
1458
+ this.contract.getSellPriceForFractionalShares(subjectAddr, fractionalAmount),
1459
+ this.contract.getSellPriceForFractionalSharesAfterFee(subjectAddr, fractionalAmount)
1460
+ ]);
1461
+ return { price: import_ethers7.ethers.formatEther(price), priceAfterFee: import_ethers7.ethers.formatEther(priceAfterFee), tickets: amount, fractionalAmount };
748
1462
  }
749
- /** Get ticket balance for a user on a subject */
750
1463
  async getBalance(subject, user) {
751
- await this.auth();
752
- return this.http.get(`/tickets/balance?subject=${subject}&user=${user}`);
1464
+ const frac = await this.contract.getMyFractionalShares(import_ethers7.ethers.getAddress(subject), import_ethers7.ethers.getAddress(user));
1465
+ return { tickets: (Number(frac) / FRACTION_SCALER).toString(), fractionalAmount: frac.toString() };
753
1466
  }
754
- /** Get ticket supply for a subject */
755
1467
  async getSupply(subject) {
756
- await this.auth();
757
- return this.http.get(`/tickets/supply?subject=${subject}`);
1468
+ const subjectAddr = import_ethers7.ethers.getAddress(subject);
1469
+ const [wholeSupply, fracSupply] = await Promise.all([
1470
+ this.contract.getSharesSupply(subjectAddr),
1471
+ this.contract.getTotalFractionalSupply(subjectAddr)
1472
+ ]);
1473
+ return { wholeSupply: wholeSupply.toString(), fractionalSupply: fracSupply.toString(), tickets: (Number(fracSupply) / FRACTION_SCALER).toString() };
758
1474
  }
759
- /** Get fee structure */
760
1475
  async getFees() {
761
- await this.auth();
762
- return this.http.get("/tickets/fees");
1476
+ const [protocol, subject, referral] = await Promise.all([
1477
+ this.contract.protocolFeePercent(),
1478
+ this.contract.subjectFeePercent(),
1479
+ this.contract.referralFeePercent()
1480
+ ]);
1481
+ const toPercent = (v) => (Number(v) / 1e16).toFixed(1) + "%";
1482
+ return { protocolFee: toPercent(protocol), subjectFee: toPercent(subject), referralFee: toPercent(referral), totalFeePercent: toPercent(protocol + subject + referral) };
763
1483
  }
764
- /** Build unsigned tx to buy tickets */
765
1484
  async buildBuyTx(wallet, subject, amount = "1") {
766
- await this.auth();
767
- return this.http.post("/tickets/buy", { wallet, subject, amount });
1485
+ const fractionalAmount = this.ticketsToFractional(amount);
1486
+ const subjectAddr = import_ethers7.ethers.getAddress(subject);
1487
+ const userAddr = import_ethers7.ethers.getAddress(wallet);
1488
+ const cost = await this.contract.getBuyPriceForFractionalSharesAfterFee(subjectAddr, fractionalAmount);
1489
+ if (cost === 0n) throw new Error("Could not get buy price \u2014 subject may not exist");
1490
+ const iface = new import_ethers7.ethers.Interface(SHARES_ABI);
1491
+ const data = iface.encodeFunctionData("buyFractionalShares", [subjectAddr, userAddr, fractionalAmount]);
1492
+ return {
1493
+ transaction: {
1494
+ to: import_ethers7.ethers.getAddress(ARENA_SHARES_CONTRACT),
1495
+ data,
1496
+ value: import_ethers7.ethers.toBeHex(cost, 32),
1497
+ chainId: CHAIN_ID,
1498
+ gasLimit: "200000",
1499
+ description: `Buy ${amount} ticket(s) for ~${import_ethers7.ethers.formatEther(cost)} AVAX`
1500
+ }
1501
+ };
768
1502
  }
769
- /** Build unsigned tx to sell tickets */
770
1503
  async buildSellTx(wallet, subject, amount = "1") {
771
- await this.auth();
772
- return this.http.post("/tickets/sell", { wallet, subject, amount });
1504
+ const fractionalAmount = this.ticketsToFractional(amount);
1505
+ const subjectAddr = import_ethers7.ethers.getAddress(subject);
1506
+ const userAddr = import_ethers7.ethers.getAddress(wallet);
1507
+ const balance = await this.contract.getMyFractionalShares(subjectAddr, userAddr);
1508
+ if (balance < BigInt(fractionalAmount)) {
1509
+ throw new Error(`Insufficient tickets: have ${Number(balance) / FRACTION_SCALER}, trying to sell ${amount}`);
1510
+ }
1511
+ const proceeds = await this.contract.getSellPriceForFractionalSharesAfterFee(subjectAddr, fractionalAmount);
1512
+ const iface = new import_ethers7.ethers.Interface(SHARES_ABI);
1513
+ const data = iface.encodeFunctionData("sellFractionalShares", [subjectAddr, userAddr, fractionalAmount]);
1514
+ return {
1515
+ transaction: {
1516
+ to: import_ethers7.ethers.getAddress(ARENA_SHARES_CONTRACT),
1517
+ data,
1518
+ value: "0x0",
1519
+ chainId: CHAIN_ID,
1520
+ gasLimit: "200000",
1521
+ description: `Sell ${amount} ticket(s) for ~${import_ethers7.ethers.formatEther(proceeds)} AVAX`
1522
+ }
1523
+ };
1524
+ }
1525
+ ticketsToFractional(tickets) {
1526
+ const num = parseFloat(tickets);
1527
+ if (isNaN(num) || num <= 0) throw new Error(`Invalid ticket amount: ${tickets}`);
1528
+ return Math.round(num * FRACTION_SCALER);
773
1529
  }
774
1530
  };
775
1531
 
776
1532
  // src/modules/social.ts
777
1533
  var SocialModule = class {
778
- constructor(http, auth, arenaApiKey) {
779
- this.http = http;
780
- this.auth = auth;
1534
+ constructor(arenaApiKey) {
781
1535
  this.arenaApiKey = arenaApiKey;
782
1536
  }
783
- headers() {
784
- const h = {};
785
- if (this.arenaApiKey) h["X-Arena-Api-Key"] = this.arenaApiKey;
786
- return h;
787
- }
788
- /** Set the Arena API key for social endpoints */
789
1537
  setArenaApiKey(key) {
790
1538
  this.arenaApiKey = key;
791
1539
  }
792
- // ─── User Discovery ──────────────────────────────────────
1540
+ async request(method, path, body, query) {
1541
+ if (!this.arenaApiKey) throw new Error("Arena API key required for social endpoints. Pass arenaApiKey in config.");
1542
+ let url = `${ARENA_SOCIAL_API}${path}`;
1543
+ if (query) {
1544
+ const params = new URLSearchParams(query);
1545
+ url += `?${params.toString()}`;
1546
+ }
1547
+ const headers = { "x-api-key": this.arenaApiKey, "Content-Type": "application/json" };
1548
+ const res = await fetch(url, { method, headers, body: body ? JSON.stringify(body) : void 0 });
1549
+ const data = await res.json();
1550
+ if (!res.ok) {
1551
+ const msg = data.message || data.error || `Arena API error ${res.status}`;
1552
+ throw new Error(msg);
1553
+ }
1554
+ return data;
1555
+ }
1556
+ // ── User Discovery ──
793
1557
  async searchUsers(q, page = 1, pageSize = 20) {
794
- await this.auth();
795
- return this.http.get(`/social/users/search?q=${encodeURIComponent(q)}&page=${page}&pageSize=${pageSize}`, void 0, this.headers());
1558
+ return this.request("GET", "/agents/user/search", void 0, { searchString: q, page: String(page), pageSize: String(pageSize) });
796
1559
  }
797
1560
  async getUserByHandle(handle) {
798
- await this.auth();
799
- return this.http.get(`/social/users/handle/${encodeURIComponent(handle)}`, void 0, this.headers());
1561
+ return this.request("GET", "/agents/user/handle", void 0, { handle });
800
1562
  }
801
1563
  async getUserById(userId) {
802
- await this.auth();
803
- return this.http.get(`/social/users/id/${userId}`, void 0, this.headers());
1564
+ return this.request("GET", "/agents/user/id", void 0, { userId });
804
1565
  }
805
1566
  async getTopUsers(page = 1, pageSize = 20) {
806
- await this.auth();
807
- return this.http.get(`/social/users/top?page=${page}&pageSize=${pageSize}`, void 0, this.headers());
1567
+ return this.request("GET", "/agents/user/top", void 0, { page: String(page), pageSize: String(pageSize) });
808
1568
  }
809
1569
  async getMe() {
810
- await this.auth();
811
- return this.http.get("/social/me", void 0, this.headers());
1570
+ return this.request("GET", "/agents/user/me");
812
1571
  }
813
- // ─── Profile ─────────────────────────────────────────────
1572
+ // ── Profile ──
814
1573
  async updateProfile(updates) {
815
- await this.auth();
816
- return this.http.patch("/social/profile", updates, this.headers());
1574
+ return this.request("PATCH", "/agents/user/profile", updates);
1575
+ }
1576
+ async updateBanner(bannerUrl) {
1577
+ return this.request("POST", "/agents/profile/banner", { bannerUrl });
817
1578
  }
818
- // ─── Follow ──────────────────────────────────────────────
1579
+ // ── Follow ──
819
1580
  async follow(userId) {
820
- await this.auth();
821
- return this.http.post("/social/follow", { userId }, this.headers());
1581
+ return this.request("POST", "/agents/follow/follow", { userId });
822
1582
  }
823
1583
  async unfollow(userId) {
824
- await this.auth();
825
- return this.http.post("/social/unfollow", { userId }, this.headers());
1584
+ return this.request("POST", "/agents/follow/unfollow", { userId });
826
1585
  }
827
- async getFollowers(userId) {
828
- await this.auth();
829
- return this.http.get(`/social/followers/${userId}`, void 0, this.headers());
1586
+ async getFollowers(userId, page = 1, pageSize = 20) {
1587
+ return this.request("GET", "/agents/follow/followers/list", void 0, { followersOfUserId: userId, pageNumber: String(page), pageSize: String(pageSize) });
830
1588
  }
831
- async getFollowing(userId) {
832
- await this.auth();
833
- return this.http.get(`/social/following/${userId}`, void 0, this.headers());
1589
+ async getFollowing(userId, page = 1, pageSize = 20) {
1590
+ return this.request("GET", "/agents/follow/following/list", void 0, { followingUserId: userId, pageNumber: String(page), pageSize: String(pageSize) });
834
1591
  }
835
- // ─── Shares ──────────────────────────────────────────────
1592
+ // ── Shares ──
836
1593
  async getSharesStats(userId) {
837
- await this.auth();
838
- return this.http.get(`/social/shares/stats/${userId}`, void 0, this.headers());
1594
+ return this.request("GET", "/agents/shares/stats", void 0, { userId });
839
1595
  }
840
- async getShareHolders(userId) {
841
- await this.auth();
842
- const q = userId ? `?userId=${userId}` : "";
843
- return this.http.get(`/social/shares/holders${q}`, void 0, this.headers());
1596
+ async getShareHolders(userId, page = 1, pageSize = 20) {
1597
+ const q = { page: String(page), pageSize: String(pageSize) };
1598
+ if (userId) q.userId = userId;
1599
+ return this.request("GET", "/agents/shares/holders", void 0, q);
844
1600
  }
845
- async getHoldings() {
846
- await this.auth();
847
- return this.http.get("/social/shares/holdings", void 0, this.headers());
1601
+ async getHoldings(page = 1, pageSize = 20) {
1602
+ return this.request("GET", "/agents/shares/holdings", void 0, { page: String(page), pageSize: String(pageSize) });
848
1603
  }
849
- // ─── Chat: Conversations ────────────────────────────────
1604
+ // ── Chat: Conversations ──
850
1605
  async getConversations(page = 1) {
851
- await this.auth();
852
- return this.http.get(`/social/chat/conversations?page=${page}`, void 0, this.headers());
1606
+ return this.request("GET", "/agents/chat/conversations", void 0, { page: String(page), pageSize: "20" });
853
1607
  }
854
1608
  async getDirectMessages() {
855
- await this.auth();
856
- return this.http.get("/social/chat/dms", void 0, this.headers());
1609
+ return this.request("GET", "/agents/chat/direct-messages", void 0, { page: "1", pageSize: "20" });
857
1610
  }
858
1611
  async getGroupChats() {
859
- await this.auth();
860
- return this.http.get("/social/chat/groups", void 0, this.headers());
1612
+ return this.request("GET", "/agents/chat/project-chats", void 0, { page: "1", pageSize: "20" });
861
1613
  }
862
1614
  async getGroup(groupId) {
863
- await this.auth();
864
- return this.http.get(`/social/chat/group/${groupId}`, void 0, this.headers());
1615
+ return this.request("GET", "/agents/chat/group", void 0, { groupId });
865
1616
  }
866
1617
  async getMembers(groupId) {
867
- await this.auth();
868
- return this.http.get(`/social/chat/members/${groupId}`, void 0, this.headers());
1618
+ return this.request("GET", "/agents/chat/members", void 0, { groupId, page: "1", pageSize: "20" });
869
1619
  }
870
1620
  async getOrCreateDM(userId) {
871
- await this.auth();
872
- return this.http.post("/social/chat/dm", { userId }, this.headers());
1621
+ return this.request("GET", "/agents/chat/group/by/user", void 0, { userId });
873
1622
  }
874
1623
  async acceptChat(groupId) {
875
- await this.auth();
876
- return this.http.post("/social/chat/accept", { groupId }, this.headers());
1624
+ return this.request("GET", "/agents/chat/accept-chat", void 0, { groupId });
877
1625
  }
878
- // ─── Chat: Messages ─────────────────────────────────────
1626
+ // ── Chat: Messages ──
879
1627
  async sendMessage(groupId, text, replyId) {
880
- await this.auth();
881
- return this.http.post("/social/chat/send", { groupId, text, replyId }, this.headers());
1628
+ const body = { groupId, text, files: [] };
1629
+ if (replyId) body.replyId = replyId;
1630
+ return this.request("POST", "/agents/chat/message", body);
882
1631
  }
883
1632
  async getMessages(groupId, after) {
884
- await this.auth();
885
- const q = after ? `?after=${after}` : "";
886
- return this.http.get(`/social/chat/messages/${groupId}${q}`, void 0, this.headers());
1633
+ const q = { groupId };
1634
+ if (after) q.timeFrom = after;
1635
+ return this.request("GET", "/agents/chat/messages/a", void 0, q);
887
1636
  }
888
1637
  async searchMessages(q, groupId) {
889
- await this.auth();
890
- const gq = groupId ? `&groupId=${groupId}` : "";
891
- return this.http.get(`/social/chat/search?q=${encodeURIComponent(q)}${gq}`, void 0, this.headers());
1638
+ const query = { searchQuery: q, limit: "20" };
1639
+ if (groupId) query.groupId = groupId;
1640
+ return this.request("GET", "/agents/chat/messages/search", void 0, query);
892
1641
  }
893
1642
  async getMentions(groupId) {
894
- await this.auth();
895
- const q = groupId ? `?groupId=${groupId}` : "";
896
- return this.http.get(`/social/chat/mentions${q}`, void 0, this.headers());
1643
+ const q = { limit: "50" };
1644
+ if (groupId) q.groupId = groupId;
1645
+ return this.request("GET", "/agents/chat/messages/mentions", void 0, q);
897
1646
  }
898
- // ─── Chat: Reactions & Pins ─────────────────────────────
1647
+ // ── Chat: Reactions & Pins ──
899
1648
  async react(messageId, groupId, reaction) {
900
- await this.auth();
901
- return this.http.post("/social/chat/react", { messageId, groupId, reaction }, this.headers());
1649
+ return this.request("POST", "/agents/chat/react", { messageId, groupId, reaction });
902
1650
  }
903
1651
  async pinMessage(messageId, groupId, isPinned = true) {
904
- await this.auth();
905
- return this.http.post("/social/chat/pin", { messageId, groupId, isPinned }, this.headers());
1652
+ return this.request("POST", "/agents/chat/message/pin", { messageId, groupId, isPinned });
906
1653
  }
907
1654
  async getPinnedMessages(groupId) {
908
- await this.auth();
909
- return this.http.get(`/social/chat/pinned/${groupId}`, void 0, this.headers());
1655
+ return this.request("GET", `/agents/chat/messages/pinned/${groupId}`);
910
1656
  }
911
- // ─── Threads ────────────────────────────────────────────
1657
+ // ── Threads ──
912
1658
  async createThread(content, replyToId) {
913
- await this.auth();
914
- return this.http.post("/social/thread", { content, replyToId }, this.headers());
1659
+ const body = { content };
1660
+ if (replyToId) body.replyToId = replyToId;
1661
+ return this.request("POST", "/agents/threads", body);
915
1662
  }
916
1663
  async likeThread(threadId) {
917
- await this.auth();
918
- return this.http.post("/social/thread/like", { threadId }, this.headers());
1664
+ return this.request("POST", "/agents/threads/like", { threadId });
919
1665
  }
920
1666
  async repost(threadId) {
921
- await this.auth();
922
- return this.http.post("/social/thread/repost", { threadId }, this.headers());
1667
+ return this.request("POST", "/agents/threads/repost", { threadId });
1668
+ }
1669
+ };
1670
+
1671
+ // src/modules/signals.ts
1672
+ var SignalsModule = class {
1673
+ assetCache = null;
1674
+ candleCache = /* @__PURE__ */ new Map();
1675
+ async hlPost(body) {
1676
+ const res = await fetch(HL_INFO, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body) });
1677
+ if (!res.ok) throw new Error(`Hyperliquid API error ${res.status}`);
1678
+ return res.json();
1679
+ }
1680
+ /** Get all asset contexts (cached 10s) */
1681
+ async getAssetContexts() {
1682
+ if (this.assetCache && Date.now() - this.assetCache.ts < 1e4) return this.assetCache.data;
1683
+ const raw = await this.hlPost({ type: "metaAndAssetCtxs" });
1684
+ const result = { meta: raw[0].universe, contexts: raw[1] };
1685
+ this.assetCache = { data: result, ts: Date.now() };
1686
+ return result;
1687
+ }
1688
+ /** Get market signal for a specific asset */
1689
+ async getMarketSignal(coin) {
1690
+ const { meta, contexts } = await this.getAssetContexts();
1691
+ const idx = meta.findIndex((m2) => m2.name.toUpperCase() === coin.toUpperCase());
1692
+ if (idx === -1) throw new Error(`Asset ${coin} not found on Hyperliquid`);
1693
+ const m = meta[idx];
1694
+ const ctx = contexts[idx];
1695
+ const price = parseFloat(ctx.markPx);
1696
+ const prevPrice = parseFloat(ctx.prevDayPx);
1697
+ const funding = parseFloat(ctx.funding);
1698
+ const change24h = price - prevPrice;
1699
+ const change24hPct = prevPrice > 0 ? change24h / prevPrice * 100 : 0;
1700
+ let fundingBias = "neutral";
1701
+ if (funding > 1e-4) fundingBias = "long-heavy";
1702
+ else if (funding < -1e-4) fundingBias = "short-heavy";
1703
+ return {
1704
+ coin: m.name,
1705
+ price,
1706
+ oraclePrice: parseFloat(ctx.oraclePx),
1707
+ change24h,
1708
+ change24hPct: Math.round(change24hPct * 100) / 100,
1709
+ volume24h: Math.round(parseFloat(ctx.dayNtlVlm)),
1710
+ openInterest: Math.round(parseFloat(ctx.openInterest)),
1711
+ fundingRate: funding,
1712
+ fundingAnnualized: Math.round(funding * 8760 * 1e4) / 100,
1713
+ fundingBias,
1714
+ maxLeverage: m.maxLeverage
1715
+ };
1716
+ }
1717
+ /** Get funding rate extremes across all markets */
1718
+ async getFundingExtremes(count = 10) {
1719
+ const { meta, contexts } = await this.getAssetContexts();
1720
+ const signals = meta.map((m, i) => this.buildSignal(m, contexts[i]));
1721
+ const sorted = [...signals].sort((a, b) => b.fundingRate - a.fundingRate);
1722
+ return { mostPositive: sorted.slice(0, count), mostNegative: sorted.slice(-count).reverse() };
1723
+ }
1724
+ /** Get candle data for technical analysis (cached 60s) */
1725
+ async getCandles(coin, interval = "1h", count = 100) {
1726
+ const cacheKey = `${coin}-${interval}`;
1727
+ const cached = this.candleCache.get(cacheKey);
1728
+ if (cached && Date.now() - cached.ts < 6e4) return cached.data.slice(-count);
1729
+ const startTime = Date.now() - count * this.intervalToMs(interval);
1730
+ const data = await this.hlPost({ type: "candleSnapshot", req: { coin: coin.toUpperCase(), interval, startTime, endTime: Date.now() } });
1731
+ const candles = data.map((c) => [c.t, parseFloat(c.o), parseFloat(c.h), parseFloat(c.l), parseFloat(c.c), parseFloat(c.v)]);
1732
+ this.candleCache.set(cacheKey, { data: candles, ts: Date.now() });
1733
+ return candles.slice(-count);
1734
+ }
1735
+ /** Compute technical signals from candle data */
1736
+ async getTechnicalSignal(coin, interval = "1h") {
1737
+ const candles = await this.getCandles(coin, interval, 100);
1738
+ const closes = candles.map((c) => c[4]);
1739
+ const highs = candles.map((c) => c[2]);
1740
+ const lows = candles.map((c) => c[3]);
1741
+ const price = closes[closes.length - 1];
1742
+ const sma20 = this.sma(closes, 20);
1743
+ const sma50 = this.sma(closes, 50);
1744
+ const rsi14 = this.rsi(closes, 14);
1745
+ let trend = "neutral";
1746
+ if (price > sma20 && price > sma50) trend = "bullish";
1747
+ else if (price < sma20 && price < sma50) trend = "bearish";
1748
+ let momentum = "moderate";
1749
+ if (rsi14 > 70 || rsi14 < 30) momentum = "strong";
1750
+ else if (rsi14 > 60 || rsi14 < 40) momentum = "moderate";
1751
+ else momentum = "weak";
1752
+ const returns = closes.slice(-21).map((c, i, arr) => i === 0 ? 0 : (c - arr[i - 1]) / arr[i - 1]);
1753
+ returns.shift();
1754
+ const volatility = Math.sqrt(returns.reduce((sum, r) => sum + r * r, 0) / returns.length) * 100;
1755
+ const resistance = Math.max(...highs.slice(-20));
1756
+ const support = Math.min(...lows.slice(-20));
1757
+ return {
1758
+ coin: coin.toUpperCase(),
1759
+ price,
1760
+ sma20: Math.round(sma20 * 100) / 100,
1761
+ sma50: Math.round(sma50 * 100) / 100,
1762
+ rsi14: Math.round(rsi14 * 100) / 100,
1763
+ trend,
1764
+ momentum,
1765
+ priceVsSma20: Math.round((price - sma20) / sma20 * 1e4) / 100,
1766
+ priceVsSma50: Math.round((price - sma50) / sma50 * 1e4) / 100,
1767
+ volatility: Math.round(volatility * 100) / 100,
1768
+ support: Math.round(support * 100) / 100,
1769
+ resistance: Math.round(resistance * 100) / 100
1770
+ };
1771
+ }
1772
+ /** Get whale positions from orderbook depth */
1773
+ async getWhalePositions(coin, minPositionUsd = 1e5) {
1774
+ const book = await this.hlPost({ type: "l2Book", coin: coin.toUpperCase() });
1775
+ const whales = [];
1776
+ for (const level of book.levels[0] ?? []) {
1777
+ const px = parseFloat(level.px);
1778
+ const sz = parseFloat(level.sz);
1779
+ const value = px * sz;
1780
+ if (value >= minPositionUsd) whales.push({ coin: coin.toUpperCase(), size: sz, entryPrice: px, side: "long", positionValue: Math.round(value) });
1781
+ }
1782
+ for (const level of book.levels[1] ?? []) {
1783
+ const px = parseFloat(level.px);
1784
+ const sz = parseFloat(level.sz);
1785
+ const value = px * sz;
1786
+ if (value >= minPositionUsd) whales.push({ coin: coin.toUpperCase(), size: sz, entryPrice: px, side: "short", positionValue: Math.round(value) });
1787
+ }
1788
+ return whales.sort((a, b) => b.positionValue - a.positionValue);
1789
+ }
1790
+ /** Full signal summary — everything an agent needs to decide */
1791
+ async summary(coin) {
1792
+ const [market, technical, whalePositions] = await Promise.all([
1793
+ this.getMarketSignal(coin),
1794
+ this.getTechnicalSignal(coin),
1795
+ this.getWhalePositions(coin)
1796
+ ]);
1797
+ const topLongs = whalePositions.filter((w) => w.side === "long").slice(0, 5);
1798
+ const topShorts = whalePositions.filter((w) => w.side === "short").slice(0, 5);
1799
+ const longValue = topLongs.reduce((s, w) => s + w.positionValue, 0);
1800
+ const shortValue = topShorts.reduce((s, w) => s + w.positionValue, 0);
1801
+ let netBias = "neutral";
1802
+ if (longValue > shortValue * 1.3) netBias = "long";
1803
+ else if (shortValue > longValue * 1.3) netBias = "short";
1804
+ const reasons = [];
1805
+ let longScore = 0;
1806
+ let shortScore = 0;
1807
+ if (technical.trend === "bullish") {
1808
+ longScore += 2;
1809
+ reasons.push("Price above SMA20 & SMA50 (bullish trend)");
1810
+ } else if (technical.trend === "bearish") {
1811
+ shortScore += 2;
1812
+ reasons.push("Price below SMA20 & SMA50 (bearish trend)");
1813
+ }
1814
+ if (technical.rsi14 < 30) {
1815
+ longScore += 2;
1816
+ reasons.push(`RSI ${technical.rsi14} \u2014 oversold`);
1817
+ } else if (technical.rsi14 > 70) {
1818
+ shortScore += 2;
1819
+ reasons.push(`RSI ${technical.rsi14} \u2014 overbought`);
1820
+ }
1821
+ if (market.fundingBias === "long-heavy") {
1822
+ shortScore += 1;
1823
+ reasons.push("Crowded long \u2014 contrarian short signal");
1824
+ } else if (market.fundingBias === "short-heavy") {
1825
+ longScore += 1;
1826
+ reasons.push("Crowded short \u2014 contrarian long signal");
1827
+ }
1828
+ if (netBias === "long") {
1829
+ longScore += 1;
1830
+ reasons.push("Whale bias: more large bids");
1831
+ } else if (netBias === "short") {
1832
+ shortScore += 1;
1833
+ reasons.push("Whale bias: more large asks");
1834
+ }
1835
+ if (market.change24hPct > 3) {
1836
+ longScore += 1;
1837
+ reasons.push(`+${market.change24hPct}% momentum`);
1838
+ } else if (market.change24hPct < -3) {
1839
+ shortScore += 1;
1840
+ reasons.push(`${market.change24hPct}% selling`);
1841
+ }
1842
+ const diff = Math.abs(longScore - shortScore);
1843
+ let direction = "wait";
1844
+ let confidence = "low";
1845
+ if (diff >= 4) {
1846
+ direction = longScore > shortScore ? "long" : "short";
1847
+ confidence = "high";
1848
+ } else if (diff >= 2) {
1849
+ direction = longScore > shortScore ? "long" : "short";
1850
+ confidence = "medium";
1851
+ } else {
1852
+ reasons.push("Mixed signals \u2014 wait for better setup.");
1853
+ }
1854
+ return {
1855
+ coin: coin.toUpperCase(),
1856
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1857
+ market,
1858
+ technical,
1859
+ whaleActivity: { topLongs, topShorts, netBias, largestPosition: whalePositions[0] ?? null },
1860
+ verdict: { direction, confidence, reasons }
1861
+ };
1862
+ }
1863
+ /** Scan for top trading opportunities */
1864
+ async scan(count = 5) {
1865
+ const [movers, funding] = await Promise.all([this.getTopMovers(10), this.getFundingExtremes(10)]);
1866
+ const candidates = /* @__PURE__ */ new Set(["BTC", "ETH", "SOL"]);
1867
+ movers.gainers.slice(0, 3).forEach((s) => candidates.add(s.coin));
1868
+ movers.losers.slice(0, 3).forEach((s) => candidates.add(s.coin));
1869
+ funding.mostPositive.slice(0, 2).forEach((s) => candidates.add(s.coin));
1870
+ funding.mostNegative.slice(0, 2).forEach((s) => candidates.add(s.coin));
1871
+ const summaries = await Promise.all(Array.from(candidates).slice(0, 15).map((c) => this.summary(c).catch(() => null)));
1872
+ const valid = summaries.filter((s) => s !== null && s.verdict.direction !== "wait");
1873
+ const order = { high: 3, medium: 2, low: 1 };
1874
+ valid.sort((a, b) => order[b.verdict.confidence] - order[a.verdict.confidence]);
1875
+ return valid.slice(0, count);
1876
+ }
1877
+ async getTopMovers(count) {
1878
+ const { meta, contexts } = await this.getAssetContexts();
1879
+ const signals = meta.map((m, i) => this.buildSignal(m, contexts[i]));
1880
+ const sorted = [...signals].sort((a, b) => b.change24hPct - a.change24hPct);
1881
+ return { gainers: sorted.slice(0, count), losers: sorted.slice(-count).reverse() };
1882
+ }
1883
+ buildSignal(m, ctx) {
1884
+ const price = parseFloat(ctx.markPx);
1885
+ const prevPrice = parseFloat(ctx.prevDayPx);
1886
+ const funding = parseFloat(ctx.funding);
1887
+ const change24hPct = prevPrice > 0 ? (price - prevPrice) / prevPrice * 100 : 0;
1888
+ let fundingBias = "neutral";
1889
+ if (funding > 1e-4) fundingBias = "long-heavy";
1890
+ else if (funding < -1e-4) fundingBias = "short-heavy";
1891
+ return {
1892
+ coin: m.name,
1893
+ price,
1894
+ oraclePrice: parseFloat(ctx.oraclePx),
1895
+ change24h: price - prevPrice,
1896
+ change24hPct: Math.round(change24hPct * 100) / 100,
1897
+ volume24h: Math.round(parseFloat(ctx.dayNtlVlm)),
1898
+ openInterest: Math.round(parseFloat(ctx.openInterest)),
1899
+ fundingRate: funding,
1900
+ fundingAnnualized: Math.round(funding * 8760 * 1e4) / 100,
1901
+ fundingBias,
1902
+ maxLeverage: m.maxLeverage
1903
+ };
1904
+ }
1905
+ sma(data, period) {
1906
+ const s = data.slice(-period);
1907
+ return s.reduce((sum, v) => sum + v, 0) / s.length;
1908
+ }
1909
+ rsi(closes, period) {
1910
+ const changes = closes.slice(-period - 1).map((c, i, arr) => i === 0 ? 0 : c - arr[i - 1]);
1911
+ changes.shift();
1912
+ let avgGain = 0;
1913
+ let avgLoss = 0;
1914
+ for (const ch of changes) {
1915
+ if (ch > 0) avgGain += ch;
1916
+ else avgLoss += Math.abs(ch);
1917
+ }
1918
+ avgGain /= period;
1919
+ avgLoss /= period;
1920
+ if (avgLoss === 0) return 100;
1921
+ return 100 - 100 / (1 + avgGain / avgLoss);
1922
+ }
1923
+ intervalToMs(interval) {
1924
+ const map = { "1m": 6e4, "5m": 3e5, "15m": 9e5, "1h": 36e5, "4h": 144e5, "1d": 864e5 };
1925
+ return map[interval] ?? 36e5;
1926
+ }
1927
+ };
1928
+
1929
+ // src/modules/market.ts
1930
+ var COINGECKO_API = "https://api.coingecko.com/api/v3";
1931
+ var MarketModule = class {
1932
+ /** Get prices for one or more coins (by CoinGecko ID) */
1933
+ async price(ids) {
1934
+ const idStr = Array.isArray(ids) ? ids.join(",") : ids;
1935
+ const res = await fetch(`${COINGECKO_API}/simple/price?ids=${idStr}&vs_currencies=usd&include_24hr_change=true&include_market_cap=true&include_24hr_vol=true`);
1936
+ if (!res.ok) throw new Error(`CoinGecko price error ${res.status}`);
1937
+ return res.json();
1938
+ }
1939
+ /** Get trending coins */
1940
+ async trending() {
1941
+ const res = await fetch(`${COINGECKO_API}/search/trending`);
1942
+ if (!res.ok) throw new Error(`CoinGecko trending error ${res.status}`);
1943
+ const data = await res.json();
1944
+ const coins = (data.coins ?? []).map((c) => ({
1945
+ id: c.item.id,
1946
+ name: c.item.name,
1947
+ symbol: c.item.symbol,
1948
+ market_cap_rank: c.item.market_cap_rank,
1949
+ thumb: c.item.thumb,
1950
+ price_btc: c.item.price_btc
1951
+ }));
1952
+ return { coins };
1953
+ }
1954
+ /** Get top coins by market cap */
1955
+ async markets(count = 20, page = 1) {
1956
+ const res = await fetch(`${COINGECKO_API}/coins/markets?vs_currency=usd&order=market_cap_desc&per_page=${count}&page=${page}&sparkline=false`);
1957
+ if (!res.ok) throw new Error(`CoinGecko markets error ${res.status}`);
1958
+ const data = await res.json();
1959
+ const coins = data.map((c) => ({
1960
+ id: c.id,
1961
+ symbol: c.symbol,
1962
+ name: c.name,
1963
+ current_price: c.current_price,
1964
+ market_cap: c.market_cap,
1965
+ price_change_percentage_24h: c.price_change_percentage_24h,
1966
+ total_volume: c.total_volume,
1967
+ market_cap_rank: c.market_cap_rank
1968
+ }));
1969
+ return { coins };
1970
+ }
1971
+ /** Search for coins by query */
1972
+ async search(query) {
1973
+ const res = await fetch(`${COINGECKO_API}/search?query=${encodeURIComponent(query)}`);
1974
+ if (!res.ok) throw new Error(`CoinGecko search error ${res.status}`);
1975
+ const data = await res.json();
1976
+ return { coins: (data.coins ?? []).map((c) => ({ id: c.id, name: c.name, symbol: c.symbol, market_cap_rank: c.market_cap_rank })) };
1977
+ }
1978
+ /** Get AVAX price specifically */
1979
+ async avaxPrice() {
1980
+ const data = await this.price("avalanche-2");
1981
+ const avax = data["avalanche-2"];
1982
+ return { usd: avax?.usd ?? 0, change24h: avax?.usd_24h_change ?? 0 };
1983
+ }
1984
+ /** Get ARENA price specifically */
1985
+ async arenaPrice() {
1986
+ const data = await this.price("arena-social");
1987
+ const arena = data["arena-social"];
1988
+ return { usd: arena?.usd ?? 0, change24h: arena?.usd_24h_change ?? 0 };
1989
+ }
1990
+ };
1991
+
1992
+ // src/modules/defi.ts
1993
+ var import_ethers8 = require("ethers");
1994
+ var SAVAX_ADDRESS = "0x2b2C81e08f1Af8835a78Bb2A90AE924ACE0eA4bE";
1995
+ var SAVAX_ABI = [
1996
+ "function submit() payable returns (uint256)",
1997
+ "function requestUnlock(uint256 shareAmount) returns (uint256)",
1998
+ "function getPooledAvaxByShares(uint256 shareAmount) view returns (uint256)",
1999
+ "function getSharesByPooledAvax(uint256 avaxAmount) view returns (uint256)",
2000
+ "function totalPooledAvax() view returns (uint256)",
2001
+ "function totalShares() view returns (uint256)",
2002
+ "function balanceOf(address) view returns (uint256)",
2003
+ "function cooldownPeriod() view returns (uint256)",
2004
+ "function exchangeRate() view returns (uint256)",
2005
+ "function approve(address,uint256) returns (bool)"
2006
+ ];
2007
+ var VAULT_ABI = [
2008
+ "function asset() view returns (address)",
2009
+ "function totalAssets() view returns (uint256)",
2010
+ "function totalSupply() view returns (uint256)",
2011
+ "function balanceOf(address) view returns (uint256)",
2012
+ "function convertToShares(uint256 assets) view returns (uint256)",
2013
+ "function convertToAssets(uint256 shares) view returns (uint256)",
2014
+ "function maxDeposit(address) view returns (uint256)",
2015
+ "function maxWithdraw(address) view returns (uint256)",
2016
+ "function previewDeposit(uint256 assets) view returns (uint256)",
2017
+ "function previewWithdraw(uint256 assets) view returns (uint256)",
2018
+ "function deposit(uint256 assets, address receiver) returns (uint256)",
2019
+ "function withdraw(uint256 assets, address receiver, address owner) returns (uint256)",
2020
+ "function decimals() view returns (uint8)",
2021
+ "function symbol() view returns (string)",
2022
+ "function name() view returns (string)"
2023
+ ];
2024
+ var ERC20_ABI2 = [
2025
+ "function balanceOf(address) view returns (uint256)",
2026
+ "function approve(address,uint256) returns (bool)",
2027
+ "function allowance(address,address) view returns (uint256)",
2028
+ "function decimals() view returns (uint8)",
2029
+ "function symbol() view returns (string)"
2030
+ ];
2031
+ var DefiModule = class {
2032
+ constructor(provider) {
2033
+ this.provider = provider;
2034
+ this.savax = new import_ethers8.ethers.Contract(import_ethers8.ethers.getAddress(SAVAX_ADDRESS), SAVAX_ABI, provider);
2035
+ }
2036
+ savax;
2037
+ // ── sAVAX Liquid Staking ──
2038
+ /** Get sAVAX staking info: exchange rate, total staked, your balance */
2039
+ async sAvaxInfo(wallet) {
2040
+ const [totalPooled, totalShares] = await Promise.all([
2041
+ this.savax.totalPooledAvax(),
2042
+ this.savax.totalShares()
2043
+ ]);
2044
+ const exchangeRate = totalShares > 0n ? import_ethers8.ethers.formatEther(totalPooled * import_ethers8.ethers.parseEther("1") / totalShares) : "1.0";
2045
+ const result = {
2046
+ exchangeRate,
2047
+ totalPooledAvax: import_ethers8.ethers.formatEther(totalPooled),
2048
+ totalShares: import_ethers8.ethers.formatEther(totalShares)
2049
+ };
2050
+ if (wallet) {
2051
+ const balance = await this.savax.balanceOf(wallet);
2052
+ const avaxValue = await this.savax.getPooledAvaxByShares(balance);
2053
+ result.balance = import_ethers8.ethers.formatEther(balance);
2054
+ result.balanceInAvax = import_ethers8.ethers.formatEther(avaxValue);
2055
+ }
2056
+ return result;
2057
+ }
2058
+ /** Quote: how much sAVAX for staking AVAX */
2059
+ async sAvaxStakeQuote(avaxAmount) {
2060
+ const avaxWei = import_ethers8.ethers.parseEther(avaxAmount);
2061
+ const shares = await this.savax.getSharesByPooledAvax(avaxWei);
2062
+ return { avaxIn: avaxAmount, savaxOut: import_ethers8.ethers.formatEther(shares) };
2063
+ }
2064
+ /** Build tx to stake AVAX → sAVAX */
2065
+ async buildSAvaxStake(avaxAmount) {
2066
+ const avaxWei = import_ethers8.ethers.parseEther(avaxAmount);
2067
+ const iface = new import_ethers8.ethers.Interface(SAVAX_ABI);
2068
+ const data = iface.encodeFunctionData("submit", []);
2069
+ return {
2070
+ transactions: [{
2071
+ to: import_ethers8.ethers.getAddress(SAVAX_ADDRESS),
2072
+ data,
2073
+ value: import_ethers8.ethers.toBeHex(avaxWei, 32),
2074
+ chainId: CHAIN_ID,
2075
+ gasLimit: "300000",
2076
+ description: `Stake ${avaxAmount} AVAX \u2192 sAVAX`
2077
+ }]
2078
+ };
2079
+ }
2080
+ /** Build tx to request unstake sAVAX → AVAX (delayed) */
2081
+ async buildSAvaxUnstake(wallet, amount) {
2082
+ let shareAmount;
2083
+ if (amount === "max") {
2084
+ shareAmount = await this.savax.balanceOf(wallet);
2085
+ } else {
2086
+ shareAmount = import_ethers8.ethers.parseEther(amount);
2087
+ }
2088
+ if (shareAmount === 0n) throw new Error("Zero sAVAX balance");
2089
+ const iface = new import_ethers8.ethers.Interface(SAVAX_ABI);
2090
+ const data = iface.encodeFunctionData("requestUnlock", [shareAmount]);
2091
+ return {
2092
+ transactions: [{
2093
+ to: import_ethers8.ethers.getAddress(SAVAX_ADDRESS),
2094
+ data,
2095
+ value: "0",
2096
+ chainId: CHAIN_ID,
2097
+ gasLimit: "300000",
2098
+ description: `Request unstake ${import_ethers8.ethers.formatEther(shareAmount)} sAVAX`
2099
+ }]
2100
+ };
2101
+ }
2102
+ // ── ERC-4626 Vaults ──
2103
+ /** Get info about any ERC-4626 vault */
2104
+ async vaultInfo(vaultAddress, wallet) {
2105
+ const vault = new import_ethers8.ethers.Contract(import_ethers8.ethers.getAddress(vaultAddress), VAULT_ABI, this.provider);
2106
+ const [name, symbol, asset, totalAssets, totalSupply, decimals] = await Promise.all([
2107
+ vault.name(),
2108
+ vault.symbol(),
2109
+ vault.asset(),
2110
+ vault.totalAssets(),
2111
+ vault.totalSupply(),
2112
+ vault.decimals()
2113
+ ]);
2114
+ const sharePrice = totalSupply > 0n ? import_ethers8.ethers.formatUnits(totalAssets * 10n ** BigInt(decimals) / totalSupply, decimals) : "1.0";
2115
+ const result = {
2116
+ name,
2117
+ symbol,
2118
+ asset,
2119
+ totalAssets: import_ethers8.ethers.formatUnits(totalAssets, decimals),
2120
+ totalSupply: import_ethers8.ethers.formatUnits(totalSupply, decimals),
2121
+ sharePrice
2122
+ };
2123
+ if (wallet) {
2124
+ const shares = await vault.balanceOf(wallet);
2125
+ const assets = shares > 0n ? await vault.convertToAssets(shares) : 0n;
2126
+ result.userShares = import_ethers8.ethers.formatUnits(shares, decimals);
2127
+ result.userAssets = import_ethers8.ethers.formatUnits(assets, decimals);
2128
+ }
2129
+ return result;
2130
+ }
2131
+ /** Quote vault deposit — how many shares for given assets */
2132
+ async vaultDepositQuote(vaultAddress, amount) {
2133
+ const vault = new import_ethers8.ethers.Contract(import_ethers8.ethers.getAddress(vaultAddress), VAULT_ABI, this.provider);
2134
+ const decimals = Number(await vault.decimals());
2135
+ const assetsWei = import_ethers8.ethers.parseUnits(amount, decimals);
2136
+ const shares = await vault.previewDeposit(assetsWei);
2137
+ return { assetsIn: amount, sharesOut: import_ethers8.ethers.formatUnits(shares, decimals) };
2138
+ }
2139
+ /** Build txs to deposit into an ERC-4626 vault: [approve, deposit] */
2140
+ async buildVaultDeposit(wallet, vaultAddress, amount) {
2141
+ const vaultAddr = import_ethers8.ethers.getAddress(vaultAddress);
2142
+ const vault = new import_ethers8.ethers.Contract(vaultAddr, VAULT_ABI, this.provider);
2143
+ const assetAddr = await vault.asset();
2144
+ const assetToken = new import_ethers8.ethers.Contract(assetAddr, ERC20_ABI2, this.provider);
2145
+ const decimals = Number(await assetToken.decimals());
2146
+ let depositAmount;
2147
+ if (amount === "max") {
2148
+ depositAmount = await assetToken.balanceOf(wallet);
2149
+ } else {
2150
+ depositAmount = import_ethers8.ethers.parseUnits(amount, decimals);
2151
+ }
2152
+ if (depositAmount === 0n) throw new Error("Zero balance to deposit");
2153
+ const erc20Iface = new import_ethers8.ethers.Interface(ERC20_ABI2);
2154
+ const approveData = erc20Iface.encodeFunctionData("approve", [vaultAddr, depositAmount]);
2155
+ const vaultIface = new import_ethers8.ethers.Interface(VAULT_ABI);
2156
+ const depositData = vaultIface.encodeFunctionData("deposit", [depositAmount, wallet]);
2157
+ return {
2158
+ transactions: [
2159
+ { to: assetAddr, data: approveData, value: "0", chainId: CHAIN_ID, gasLimit: "60000", description: `Approve asset for vault deposit` },
2160
+ { to: vaultAddr, data: depositData, value: "0", chainId: CHAIN_ID, gasLimit: "300000", description: `Deposit ${import_ethers8.ethers.formatUnits(depositAmount, decimals)} into vault` }
2161
+ ]
2162
+ };
2163
+ }
2164
+ /** Build tx to withdraw from an ERC-4626 vault */
2165
+ async buildVaultWithdraw(wallet, vaultAddress, amount) {
2166
+ const vaultAddr = import_ethers8.ethers.getAddress(vaultAddress);
2167
+ const vault = new import_ethers8.ethers.Contract(vaultAddr, VAULT_ABI, this.provider);
2168
+ const decimals = Number(await vault.decimals());
2169
+ let withdrawAmount;
2170
+ if (amount === "max") {
2171
+ const shares = await vault.balanceOf(wallet);
2172
+ withdrawAmount = await vault.convertToAssets(shares);
2173
+ } else {
2174
+ withdrawAmount = import_ethers8.ethers.parseUnits(amount, decimals);
2175
+ }
2176
+ if (withdrawAmount === 0n) throw new Error("Nothing to withdraw");
2177
+ const iface = new import_ethers8.ethers.Interface(VAULT_ABI);
2178
+ const data = iface.encodeFunctionData("withdraw", [withdrawAmount, wallet, wallet]);
2179
+ return {
2180
+ transactions: [{
2181
+ to: vaultAddr,
2182
+ data,
2183
+ value: "0",
2184
+ chainId: CHAIN_ID,
2185
+ gasLimit: "300000",
2186
+ description: `Withdraw ${import_ethers8.ethers.formatUnits(withdrawAmount, decimals)} from vault`
2187
+ }]
2188
+ };
923
2189
  }
924
2190
  };
925
2191
 
926
2192
  // src/client.ts
927
- var DEFAULT_BASE_URL = "https://brave-alignment-production-1706.up.railway.app";
928
- var LogiqicalClient = class {
2193
+ var Logiqical = class _Logiqical {
929
2194
  /** Buy/sell ARENA tokens via LFJ DEX */
930
2195
  swap;
931
2196
  /** Stake ARENA tokens for rewards */
932
2197
  staking;
933
2198
  /** Discover, research, and trade launchpad tokens on bonding curves */
934
2199
  launchpad;
935
- /** Swap any Avalanche token via LFJ + Arena DEX */
2200
+ /** Swap any Avalanche token via LFJ DEX */
936
2201
  dex;
937
2202
  /** Trade perpetual futures on Hyperliquid via Arena */
938
2203
  perps;
939
- /** Cross-chain token bridging via Li.Fi — any token, any chain */
2204
+ /** Cross-chain token bridging via Li.Fi */
940
2205
  bridge;
941
- /** Buy/sell Arena tickets (keys) — gives access to private chat rooms */
2206
+ /** Buy/sell Arena tickets */
942
2207
  tickets;
943
2208
  /** Arena social: chat, DMs, posts, follow, user discovery */
944
2209
  social;
945
- http;
946
- wallet;
947
- agentName;
948
- registrationPromise = null;
2210
+ /** Market signals intelligence — scan, funding rates, whale tracking */
2211
+ signals;
2212
+ /** Market data — prices, trending, top coins via CoinGecko */
2213
+ market;
2214
+ /** DeFi — sAVAX liquid staking + ERC-4626 vaults */
2215
+ defi;
2216
+ /** The agent's wallet address */
2217
+ address;
2218
+ /** The local wallet (null if read-only mode) */
2219
+ agentWallet;
2220
+ /** The JSON-RPC provider for direct chain reads */
2221
+ provider;
2222
+ /** The spending policy engine */
2223
+ policyEngine;
2224
+ config;
949
2225
  constructor(config) {
950
- this.wallet = config.wallet;
951
- this.agentName = config.name ?? "agent";
952
- this.http = new HttpClient(config.baseUrl ?? DEFAULT_BASE_URL, config.apiKey);
953
- const auth = () => this.ensureRegistered();
954
- this.swap = new SwapModule(this.http, auth);
955
- this.staking = new StakingModule(this.http, auth);
956
- this.launchpad = new LaunchpadModule(this.http, auth);
957
- this.dex = new DexModule(this.http, auth);
958
- this.perps = new PerpsModule(this.http, auth);
959
- this.bridge = new BridgeModule(this.http, auth);
960
- this.tickets = new TicketsModule(this.http, auth);
961
- this.social = new SocialModule(this.http, auth);
962
- }
963
- /** The API key (available after first call or if provided in config) */
964
- get apiKey() {
965
- return this.http.getApiKey();
2226
+ this.config = config;
2227
+ this.policyEngine = new PolicyEngine(config.policy);
2228
+ if (config.privateKey) {
2229
+ this.agentWallet = AgentWallet.fromPrivateKey(config.privateKey, {
2230
+ network: config.network,
2231
+ rpcUrl: config.rpcUrl
2232
+ });
2233
+ this.provider = this.agentWallet.provider;
2234
+ this.address = this.agentWallet.address;
2235
+ } else if (config.mnemonic) {
2236
+ const hdWallet = import_ethers9.ethers.HDNodeWallet.fromPhrase(config.mnemonic);
2237
+ this.agentWallet = AgentWallet.fromPrivateKey(hdWallet.privateKey, {
2238
+ network: config.network,
2239
+ rpcUrl: config.rpcUrl
2240
+ });
2241
+ this.provider = this.agentWallet.provider;
2242
+ this.address = this.agentWallet.address;
2243
+ } else if (config.wallet) {
2244
+ this.agentWallet = null;
2245
+ const { provider } = _Logiqical.resolveProvider(config);
2246
+ this.provider = provider;
2247
+ this.address = config.wallet;
2248
+ } else {
2249
+ throw new Error("Provide privateKey, mnemonic, or wallet (read-only).");
2250
+ }
2251
+ this.swap = new SwapModule(this.provider);
2252
+ this.staking = new StakingModule(this.provider);
2253
+ this.launchpad = new LaunchpadModule(this.provider);
2254
+ this.dex = new DexModule(this.provider);
2255
+ this.tickets = new TicketsModule(this.provider);
2256
+ this.perps = new PerpsModule(config.arenaApiKey);
2257
+ this.social = new SocialModule(config.arenaApiKey);
2258
+ this.bridge = new BridgeModule();
2259
+ this.signals = new SignalsModule();
2260
+ this.market = new MarketModule();
2261
+ this.defi = new DefiModule(this.provider);
2262
+ }
2263
+ // ── Factory Methods ──
2264
+ /** Generate a brand new agent wallet */
2265
+ static generate(config = {}) {
2266
+ const wallet = AgentWallet.generate({ network: config.network, rpcUrl: config.rpcUrl });
2267
+ return new _Logiqical({ ...config, privateKey: wallet.privateKey });
2268
+ }
2269
+ /** Boot from encrypted keystore — creates wallet on first run, loads on subsequent runs */
2270
+ static async boot(config = {}) {
2271
+ const wallet = await AgentWallet.boot({
2272
+ network: config.network,
2273
+ rpcUrl: config.rpcUrl,
2274
+ password: config.password,
2275
+ keystoreName: config.keystoreName
2276
+ });
2277
+ return new _Logiqical({ ...config, privateKey: wallet.privateKey });
966
2278
  }
2279
+ // ── Core Agent Operations ──
967
2280
  /**
968
- * Broadcast a signed transaction to the Avalanche network.
969
- * @param signedTx - Signed transaction hex string
970
- * @returns Transaction hash
2281
+ * Execute any module result policy check + simulate + sign + broadcast.
2282
+ *
2283
+ * ```ts
2284
+ * await agent.execute(agent.dex.buildSwap(agent.address, "AVAX", "USDC", "1.0"));
2285
+ * await agent.execute(agent.launchpad.buildBuy(agent.address, "42", "0.5"));
2286
+ * ```
971
2287
  */
972
- async broadcast(signedTx) {
973
- await this.ensureRegistered();
974
- return this.http.post("/broadcast", { signedTx });
2288
+ async execute(resultOrPromise, confirmations = 1) {
2289
+ this.requireWallet("execute");
2290
+ const result = await resultOrPromise;
2291
+ const txs = result.transactions ?? (result.transaction ? [result.transaction] : []);
2292
+ if (txs.length === 0) throw new Error("No transactions to execute.");
2293
+ const results = [];
2294
+ for (const utx of txs) {
2295
+ this.policyEngine.check(utx);
2296
+ if (this.policyEngine.shouldSimulate) {
2297
+ await this.simulate(utx);
2298
+ }
2299
+ if (this.policyEngine.isDryRun) {
2300
+ results.push({
2301
+ hash: "0x_dry_run",
2302
+ receipt: { status: 1, blockNumber: 0, gasUsed: "0", transactionHash: "0x_dry_run" }
2303
+ });
2304
+ continue;
2305
+ }
2306
+ const txResponse = await this.agentWallet.signAndBroadcast(utx);
2307
+ const receipt = await txResponse.wait(confirmations);
2308
+ if (!receipt) throw new Error(`Transaction ${txResponse.hash} failed \u2014 no receipt.`);
2309
+ const value = utx.value ? BigInt(utx.value) : 0n;
2310
+ if (value > 0n) this.policyEngine.recordSpend(value);
2311
+ results.push({
2312
+ hash: txResponse.hash,
2313
+ receipt: {
2314
+ status: receipt.status ?? 0,
2315
+ blockNumber: receipt.blockNumber,
2316
+ gasUsed: receipt.gasUsed.toString(),
2317
+ transactionHash: receipt.hash
2318
+ }
2319
+ });
2320
+ }
2321
+ return results;
975
2322
  }
976
2323
  /**
977
- * Get the full agent instructions document describes all available capabilities.
2324
+ * Call any smart contract methodpolicy check + simulate + sign + broadcast.
2325
+ *
2326
+ * ```ts
2327
+ * await agent.call({
2328
+ * contract: "0x...",
2329
+ * abi: ["function transfer(address,uint256) returns (bool)"],
2330
+ * method: "transfer",
2331
+ * args: ["0xRecipient", ethers.parseUnits("100", 18)],
2332
+ * });
2333
+ * ```
978
2334
  */
979
- async getInstructions() {
980
- await this.ensureRegistered();
981
- return this.http.get("/agent-instructions");
2335
+ async call(intent) {
2336
+ this.requireWallet("call");
2337
+ const iface = new import_ethers9.ethers.Interface(intent.abi);
2338
+ const data = iface.encodeFunctionData(intent.method, intent.args ?? []);
2339
+ const valueWei = intent.value ? import_ethers9.ethers.parseEther(intent.value).toString() : "0";
2340
+ const policyTx = { to: intent.contract, data, value: valueWei, chainId: 43114 };
2341
+ this.policyEngine.check(policyTx);
2342
+ if (this.policyEngine.shouldSimulate) {
2343
+ await this.simulate(policyTx);
2344
+ }
2345
+ if (this.policyEngine.isDryRun) {
2346
+ return { hash: "0x_dry_run", receipt: { status: 1, blockNumber: 0, gasUsed: "0", transactionHash: "0x_dry_run" } };
2347
+ }
2348
+ const contract = new import_ethers9.Contract(
2349
+ import_ethers9.ethers.getAddress(intent.contract),
2350
+ intent.abi,
2351
+ this.agentWallet.wallet
2352
+ );
2353
+ const tx = await contract[intent.method](
2354
+ ...intent.args ?? [],
2355
+ {
2356
+ value: intent.value ? import_ethers9.ethers.parseEther(intent.value) : void 0,
2357
+ gasLimit: intent.gasLimit ?? void 0
2358
+ }
2359
+ );
2360
+ const receipt = await tx.wait(1);
2361
+ if (!receipt) throw new Error(`Transaction ${tx.hash} failed \u2014 no receipt.`);
2362
+ if (intent.value) {
2363
+ this.policyEngine.recordSpend(import_ethers9.ethers.parseEther(intent.value));
2364
+ }
2365
+ return {
2366
+ hash: tx.hash,
2367
+ receipt: {
2368
+ status: receipt.status ?? 0,
2369
+ blockNumber: receipt.blockNumber,
2370
+ gasUsed: receipt.gasUsed.toString(),
2371
+ transactionHash: receipt.hash
2372
+ }
2373
+ };
2374
+ }
2375
+ /** Send native token (AVAX) — policy check + simulate + sign + broadcast */
2376
+ async send(to, amount) {
2377
+ this.requireWallet("send");
2378
+ const valueWei = import_ethers9.ethers.parseEther(amount);
2379
+ const policyTx = { to, data: "0x", value: valueWei.toString(), chainId: 43114 };
2380
+ this.policyEngine.check(policyTx);
2381
+ if (this.policyEngine.isDryRun) {
2382
+ return { hash: "0x_dry_run", receipt: { status: 1, blockNumber: 0, gasUsed: "0", transactionHash: "0x_dry_run" } };
2383
+ }
2384
+ const tx = await this.agentWallet.send(to, amount);
2385
+ const receipt = await tx.wait(1);
2386
+ if (!receipt) throw new Error(`Transaction ${tx.hash} failed \u2014 no receipt.`);
2387
+ this.policyEngine.recordSpend(valueWei);
2388
+ return {
2389
+ hash: tx.hash,
2390
+ receipt: {
2391
+ status: receipt.status ?? 0,
2392
+ blockNumber: receipt.blockNumber,
2393
+ gasUsed: receipt.gasUsed.toString(),
2394
+ transactionHash: receipt.hash
2395
+ }
2396
+ };
2397
+ }
2398
+ /** Simulate a transaction via eth_call — throws PolicyError if it would revert */
2399
+ async simulate(tx) {
2400
+ try {
2401
+ await this.provider.call({
2402
+ to: tx.to,
2403
+ data: tx.data,
2404
+ value: tx.value ? BigInt(tx.value) : void 0,
2405
+ from: this.address
2406
+ });
2407
+ } catch (e) {
2408
+ throw new PolicyError(
2409
+ `Simulation failed: ${e.reason || e.message || "transaction would revert"}`,
2410
+ "SIMULATION_FAILED"
2411
+ );
2412
+ }
982
2413
  }
983
- /**
984
- * Check if the API is healthy and responsive.
985
- */
986
- async health() {
987
- return this.http.get("/health", void 0, true);
2414
+ // ── Policy ──
2415
+ /** Get the current spending policy */
2416
+ getPolicy() {
2417
+ return this.policyEngine.getPolicy();
2418
+ }
2419
+ /** Replace the entire spending policy */
2420
+ setPolicy(policy) {
2421
+ this.policyEngine.setPolicy(policy);
2422
+ }
2423
+ /** Update specific policy fields */
2424
+ updatePolicy(updates) {
2425
+ this.policyEngine.updatePolicy(updates);
2426
+ }
2427
+ /** Get budget status: spent this hour, today, remaining */
2428
+ getBudgetStatus() {
2429
+ return this.policyEngine.getBudgetStatus();
2430
+ }
2431
+ // ── Read Operations (no signing needed) ──
2432
+ /** Check if this client has a local wallet for signing */
2433
+ get canSign() {
2434
+ return this.agentWallet !== null;
2435
+ }
2436
+ /** The private key (only available in wallet mode) */
2437
+ get privateKey() {
2438
+ this.requireWallet("privateKey");
2439
+ return this.agentWallet.privateKey;
2440
+ }
2441
+ /** Get native token balance (formatted) */
2442
+ async getBalance() {
2443
+ if (this.agentWallet) return this.agentWallet.getBalance();
2444
+ const balance = await this.provider.getBalance(this.address);
2445
+ return import_ethers9.ethers.formatEther(balance);
2446
+ }
2447
+ /** Sign a message */
2448
+ async signMessage(message) {
2449
+ this.requireWallet("signMessage");
2450
+ return this.agentWallet.signMessage(message);
2451
+ }
2452
+ /** Sign EIP-712 typed data */
2453
+ async signTypedData(domain, types, value) {
2454
+ this.requireWallet("signTypedData");
2455
+ return this.agentWallet.signTypedData(domain, types, value);
2456
+ }
2457
+ /** Switch to a different network (returns new Logiqical instance with same keys) */
2458
+ switchNetwork(network) {
2459
+ this.requireWallet("switchNetwork");
2460
+ return new _Logiqical({
2461
+ ...this.config,
2462
+ privateKey: this.agentWallet.privateKey,
2463
+ mnemonic: void 0,
2464
+ wallet: void 0,
2465
+ network
2466
+ });
988
2467
  }
989
- /**
990
- * Manually register and get an API key.
991
- * Usually not needed — the client auto-registers on first call.
992
- */
993
- async register() {
994
- const data = await this.http.get("/register", {
995
- wallet: this.wallet,
996
- name: this.agentName
997
- }, true);
998
- const apiKey = data.apiKey || data.key;
999
- this.http.setApiKey(apiKey);
1000
- return { apiKey, wallet: data.wallet, name: data.name };
1001
- }
1002
- async ensureRegistered() {
1003
- if (this.http.getApiKey()) return;
1004
- if (!this.registrationPromise) {
1005
- this.registrationPromise = this.register().then(() => {
1006
- }).catch((e) => {
1007
- this.registrationPromise = null;
1008
- throw e;
1009
- });
2468
+ /** Save the wallet to an encrypted keystore file */
2469
+ async saveKeystore(password, name) {
2470
+ this.requireWallet("saveKeystore");
2471
+ return this.agentWallet.saveKeystore(password, name);
2472
+ }
2473
+ // ── Low-level (advanced — bypasses policy) ──
2474
+ /** Sign and broadcast a single unsigned tx — bypasses policy engine */
2475
+ async signAndBroadcast(unsignedTx) {
2476
+ this.requireWallet("signAndBroadcast");
2477
+ return this.agentWallet.signAndBroadcast(unsignedTx);
2478
+ }
2479
+ /** Sign and broadcast multiple unsigned txs — bypasses policy engine */
2480
+ async signAndBroadcastAll(unsignedTxs, confirmations = 1) {
2481
+ this.requireWallet("signAndBroadcastAll");
2482
+ return this.agentWallet.signAndBroadcastAll(unsignedTxs, confirmations);
2483
+ }
2484
+ // ── Internal ──
2485
+ requireWallet(method) {
2486
+ if (!this.agentWallet) throw new Error(`No local wallet \u2014 cannot ${method}(). Provide privateKey or use Logiqical.boot().`);
2487
+ }
2488
+ static resolveProvider(config) {
2489
+ const networkKey = config.network ?? "avalanche";
2490
+ const chain = CHAINS[networkKey];
2491
+ if (!chain && !config.rpcUrl) {
2492
+ throw new Error(`Unknown network "${networkKey}". Use: ${Object.keys(CHAINS).join(", ")} \u2014 or provide rpcUrl.`);
1010
2493
  }
1011
- return this.registrationPromise;
2494
+ const rpcUrl = config.rpcUrl ?? chain.rpcUrl;
2495
+ const chainId = chain?.chainId ?? void 0;
2496
+ return { provider: new import_ethers9.JsonRpcProvider(rpcUrl, chainId) };
2497
+ }
2498
+ };
2499
+ var LogiqicalClient = Logiqical;
2500
+
2501
+ // src/errors.ts
2502
+ var LogiqicalError = class _LogiqicalError extends Error {
2503
+ constructor(message, code, cause) {
2504
+ super(message);
2505
+ this.code = code;
2506
+ this.cause = cause;
2507
+ this.name = "LogiqicalError";
2508
+ }
2509
+ static from(e, code = "UNKNOWN") {
2510
+ if (e instanceof _LogiqicalError) return e;
2511
+ const cause = e instanceof Error ? e : new Error(String(e));
2512
+ return new _LogiqicalError(cause.message, code, cause);
1012
2513
  }
1013
2514
  };
1014
2515
  // Annotate the CommonJS export names for ESM import in node:
1015
2516
  0 && (module.exports = {
2517
+ AgentWallet,
1016
2518
  BridgeModule,
2519
+ CHAINS,
2520
+ DefiModule,
1017
2521
  DexModule,
1018
2522
  LaunchpadModule,
1019
- LogiqicalAuthError,
2523
+ Logiqical,
1020
2524
  LogiqicalClient,
1021
2525
  LogiqicalError,
2526
+ MarketModule,
1022
2527
  PerpsModule,
2528
+ PolicyEngine,
2529
+ PolicyError,
2530
+ SignalsModule,
1023
2531
  SocialModule,
1024
2532
  StakingModule,
1025
2533
  SwapModule,
1026
2534
  TicketsModule
1027
2535
  });
2536
+ //# sourceMappingURL=index.js.map