logiqical 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.mjs CHANGED
@@ -4,7 +4,7 @@
4
4
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
5
 
6
6
  // src/client.ts
7
- import { ethers as ethers8, JsonRpcProvider as JsonRpcProvider2, Contract } from "ethers";
7
+ import { ethers as ethers9, JsonRpcProvider as JsonRpcProvider3, Contract as Contract2 } from "ethers";
8
8
 
9
9
  // src/wallet.ts
10
10
  import {
@@ -445,6 +445,10 @@ var FRACTION_SCALER = 100;
445
445
  var ARENA_SOCIAL_API = "https://api.starsarena.com";
446
446
  var LIFI_API = "https://li.quest/v1";
447
447
  var HL_INFO = "https://api.hyperliquid.xyz/info";
448
+ var HL_DEPOSIT_ADDRESS = "0x2Df1c51E09aECF9cacB7bc98cB1742757f163dF7";
449
+ var USDC_ARBITRUM = "0xaf88d065e77c8cC2239327C5EDb3A432268e5831";
450
+ var ARBITRUM_CHAIN_ID = 42161;
451
+ var ARBITRUM_RPC = "https://arb1.arbitrum.io/rpc";
448
452
  var DEFAULT_SLIPPAGE_BPS = 500;
449
453
  var ERC20_ABI = [
450
454
  "function balanceOf(address owner) view returns (uint256)",
@@ -1141,6 +1145,7 @@ var DexModule = class {
1141
1145
  };
1142
1146
 
1143
1147
  // src/modules/perps.ts
1148
+ import { ethers as ethers6, JsonRpcProvider as JsonRpcProvider2, Contract } from "ethers";
1144
1149
  var PerpsModule = class {
1145
1150
  constructor(arenaApiKey) {
1146
1151
  this.arenaApiKey = arenaApiKey;
@@ -1234,6 +1239,51 @@ var PerpsModule = class {
1234
1239
  async getOpenOrders(wallet) {
1235
1240
  return this.hlPost({ type: "openOrders", user: wallet });
1236
1241
  }
1242
+ // ── USDC Deposit to Hyperliquid (on Arbitrum) ──
1243
+ /** Check USDC balance on Arbitrum */
1244
+ async getArbitrumUSDCBalance(wallet) {
1245
+ const provider = new JsonRpcProvider2(ARBITRUM_RPC, ARBITRUM_CHAIN_ID);
1246
+ const usdc = new Contract(USDC_ARBITRUM, ERC20_ABI, provider);
1247
+ const balance = await usdc.balanceOf(wallet);
1248
+ return ethers6.formatUnits(balance, 6);
1249
+ }
1250
+ /** Check ETH balance on Arbitrum (needed for gas) */
1251
+ async getArbitrumETHBalance(wallet) {
1252
+ const provider = new JsonRpcProvider2(ARBITRUM_RPC, ARBITRUM_CHAIN_ID);
1253
+ const balance = await provider.getBalance(wallet);
1254
+ return ethers6.formatEther(balance);
1255
+ }
1256
+ /**
1257
+ * Build a USDC deposit transaction for Hyperliquid on Arbitrum.
1258
+ * Returns an unsigned tx — use with agent.execute() after switching to Arbitrum network.
1259
+ *
1260
+ * Flow: agent.switchNetwork("arbitrum") → agent.execute(agent.perps.buildDepositUSDC(...))
1261
+ */
1262
+ buildDepositUSDC(amount) {
1263
+ const iface = new ethers6.Interface(["function transfer(address to, uint256 amount) returns (bool)"]);
1264
+ const amountWei = ethers6.parseUnits(amount, 6);
1265
+ const data = iface.encodeFunctionData("transfer", [HL_DEPOSIT_ADDRESS, amountWei]);
1266
+ return {
1267
+ transaction: {
1268
+ to: USDC_ARBITRUM,
1269
+ data,
1270
+ value: "0",
1271
+ chainId: ARBITRUM_CHAIN_ID,
1272
+ description: `Deposit ${amount} USDC to Hyperliquid`
1273
+ }
1274
+ };
1275
+ }
1276
+ /** Get deposit info (addresses + chain details) */
1277
+ getDepositInfo() {
1278
+ return {
1279
+ chain: "Arbitrum One",
1280
+ chainId: ARBITRUM_CHAIN_ID,
1281
+ usdcAddress: USDC_ARBITRUM,
1282
+ depositAddress: HL_DEPOSIT_ADDRESS,
1283
+ decimals: 6,
1284
+ tip: "Bridge USDC to Arbitrum first (use bridge module), then deposit to Hyperliquid"
1285
+ };
1286
+ }
1237
1287
  };
1238
1288
 
1239
1289
  // src/modules/bridge.ts
@@ -1390,37 +1440,37 @@ var BridgeModule = class {
1390
1440
  };
1391
1441
 
1392
1442
  // src/modules/tickets.ts
1393
- import { ethers as ethers6 } from "ethers";
1443
+ import { ethers as ethers7 } from "ethers";
1394
1444
  var TicketsModule = class {
1395
1445
  constructor(provider) {
1396
1446
  this.provider = provider;
1397
- this.contract = new ethers6.Contract(ethers6.getAddress(ARENA_SHARES_CONTRACT), SHARES_ABI, provider);
1447
+ this.contract = new ethers7.Contract(ethers7.getAddress(ARENA_SHARES_CONTRACT), SHARES_ABI, provider);
1398
1448
  }
1399
1449
  contract;
1400
1450
  async getBuyPrice(subject, amount = "1") {
1401
1451
  const fractionalAmount = this.ticketsToFractional(amount);
1402
- const subjectAddr = ethers6.getAddress(subject);
1452
+ const subjectAddr = ethers7.getAddress(subject);
1403
1453
  const [price, priceWithFee] = await Promise.all([
1404
1454
  this.contract.getBuyPriceForFractionalShares(subjectAddr, fractionalAmount),
1405
1455
  this.contract.getBuyPriceForFractionalSharesAfterFee(subjectAddr, fractionalAmount)
1406
1456
  ]);
1407
- return { price: ethers6.formatEther(price), priceWithFee: ethers6.formatEther(priceWithFee), tickets: amount, fractionalAmount };
1457
+ return { price: ethers7.formatEther(price), priceWithFee: ethers7.formatEther(priceWithFee), tickets: amount, fractionalAmount };
1408
1458
  }
1409
1459
  async getSellPrice(subject, amount = "1") {
1410
1460
  const fractionalAmount = this.ticketsToFractional(amount);
1411
- const subjectAddr = ethers6.getAddress(subject);
1461
+ const subjectAddr = ethers7.getAddress(subject);
1412
1462
  const [price, priceAfterFee] = await Promise.all([
1413
1463
  this.contract.getSellPriceForFractionalShares(subjectAddr, fractionalAmount),
1414
1464
  this.contract.getSellPriceForFractionalSharesAfterFee(subjectAddr, fractionalAmount)
1415
1465
  ]);
1416
- return { price: ethers6.formatEther(price), priceAfterFee: ethers6.formatEther(priceAfterFee), tickets: amount, fractionalAmount };
1466
+ return { price: ethers7.formatEther(price), priceAfterFee: ethers7.formatEther(priceAfterFee), tickets: amount, fractionalAmount };
1417
1467
  }
1418
1468
  async getBalance(subject, user) {
1419
- const frac = await this.contract.getMyFractionalShares(ethers6.getAddress(subject), ethers6.getAddress(user));
1469
+ const frac = await this.contract.getMyFractionalShares(ethers7.getAddress(subject), ethers7.getAddress(user));
1420
1470
  return { tickets: (Number(frac) / FRACTION_SCALER).toString(), fractionalAmount: frac.toString() };
1421
1471
  }
1422
1472
  async getSupply(subject) {
1423
- const subjectAddr = ethers6.getAddress(subject);
1473
+ const subjectAddr = ethers7.getAddress(subject);
1424
1474
  const [wholeSupply, fracSupply] = await Promise.all([
1425
1475
  this.contract.getSharesSupply(subjectAddr),
1426
1476
  this.contract.getTotalFractionalSupply(subjectAddr)
@@ -1438,42 +1488,42 @@ var TicketsModule = class {
1438
1488
  }
1439
1489
  async buildBuyTx(wallet, subject, amount = "1") {
1440
1490
  const fractionalAmount = this.ticketsToFractional(amount);
1441
- const subjectAddr = ethers6.getAddress(subject);
1442
- const userAddr = ethers6.getAddress(wallet);
1491
+ const subjectAddr = ethers7.getAddress(subject);
1492
+ const userAddr = ethers7.getAddress(wallet);
1443
1493
  const cost = await this.contract.getBuyPriceForFractionalSharesAfterFee(subjectAddr, fractionalAmount);
1444
1494
  if (cost === 0n) throw new Error("Could not get buy price \u2014 subject may not exist");
1445
- const iface = new ethers6.Interface(SHARES_ABI);
1495
+ const iface = new ethers7.Interface(SHARES_ABI);
1446
1496
  const data = iface.encodeFunctionData("buyFractionalShares", [subjectAddr, userAddr, fractionalAmount]);
1447
1497
  return {
1448
1498
  transaction: {
1449
- to: ethers6.getAddress(ARENA_SHARES_CONTRACT),
1499
+ to: ethers7.getAddress(ARENA_SHARES_CONTRACT),
1450
1500
  data,
1451
- value: ethers6.toBeHex(cost, 32),
1501
+ value: ethers7.toBeHex(cost, 32),
1452
1502
  chainId: CHAIN_ID,
1453
1503
  gasLimit: "200000",
1454
- description: `Buy ${amount} ticket(s) for ~${ethers6.formatEther(cost)} AVAX`
1504
+ description: `Buy ${amount} ticket(s) for ~${ethers7.formatEther(cost)} AVAX`
1455
1505
  }
1456
1506
  };
1457
1507
  }
1458
1508
  async buildSellTx(wallet, subject, amount = "1") {
1459
1509
  const fractionalAmount = this.ticketsToFractional(amount);
1460
- const subjectAddr = ethers6.getAddress(subject);
1461
- const userAddr = ethers6.getAddress(wallet);
1510
+ const subjectAddr = ethers7.getAddress(subject);
1511
+ const userAddr = ethers7.getAddress(wallet);
1462
1512
  const balance = await this.contract.getMyFractionalShares(subjectAddr, userAddr);
1463
1513
  if (balance < BigInt(fractionalAmount)) {
1464
1514
  throw new Error(`Insufficient tickets: have ${Number(balance) / FRACTION_SCALER}, trying to sell ${amount}`);
1465
1515
  }
1466
1516
  const proceeds = await this.contract.getSellPriceForFractionalSharesAfterFee(subjectAddr, fractionalAmount);
1467
- const iface = new ethers6.Interface(SHARES_ABI);
1517
+ const iface = new ethers7.Interface(SHARES_ABI);
1468
1518
  const data = iface.encodeFunctionData("sellFractionalShares", [subjectAddr, userAddr, fractionalAmount]);
1469
1519
  return {
1470
1520
  transaction: {
1471
- to: ethers6.getAddress(ARENA_SHARES_CONTRACT),
1521
+ to: ethers7.getAddress(ARENA_SHARES_CONTRACT),
1472
1522
  data,
1473
1523
  value: "0x0",
1474
1524
  chainId: CHAIN_ID,
1475
1525
  gasLimit: "200000",
1476
- description: `Sell ${amount} ticket(s) for ~${ethers6.formatEther(proceeds)} AVAX`
1526
+ description: `Sell ${amount} ticket(s) for ~${ethers7.formatEther(proceeds)} AVAX`
1477
1527
  }
1478
1528
  };
1479
1529
  }
@@ -1492,6 +1542,53 @@ var SocialModule = class {
1492
1542
  setArenaApiKey(key) {
1493
1543
  this.arenaApiKey = key;
1494
1544
  }
1545
+ // ── Agent Registration (no auth needed — returns API key) ──
1546
+ /**
1547
+ * Register a new AI agent on Arena. Returns API key (shown once — save immediately).
1548
+ * After registration, owner must claim by posting: "I'm claiming my AI Agent \"<name>\"\nVerification Code: <code>"
1549
+ */
1550
+ static async registerAgent(opts) {
1551
+ const res = await fetch(`${ARENA_SOCIAL_API}/agents/register`, {
1552
+ method: "POST",
1553
+ headers: { "Content-Type": "application/json" },
1554
+ body: JSON.stringify(opts)
1555
+ });
1556
+ const data = await res.json();
1557
+ if (!res.ok) throw new Error(data.message || data.error || `Registration failed (${res.status})`);
1558
+ return data;
1559
+ }
1560
+ // ── Feed Auto-Posting (trade updates) ──
1561
+ /** Post a formatted trade update to the Arena feed */
1562
+ async postTradeUpdate(trade) {
1563
+ const lines = [];
1564
+ const emoji = {
1565
+ buy: "\u{1F7E2}",
1566
+ sell: "\u{1F534}",
1567
+ swap: "\u{1F504}",
1568
+ bridge: "\u{1F309}",
1569
+ stake: "\u{1F512}",
1570
+ long: "\u{1F4C8}",
1571
+ short: "\u{1F4C9}",
1572
+ close: "\u2705"
1573
+ };
1574
+ const icon = emoji[trade.action] || "\u{1F4B0}";
1575
+ if (trade.action === "swap" && trade.fromToken && trade.toToken) {
1576
+ lines.push(`${icon} Swapped ${trade.amount || ""} ${trade.fromToken} \u2192 ${trade.toToken}`);
1577
+ } else if (trade.action === "bridge" && trade.fromToken) {
1578
+ lines.push(`${icon} Bridged ${trade.amount || ""} ${trade.fromToken}`);
1579
+ } else if (trade.action === "long" || trade.action === "short") {
1580
+ lines.push(`${icon} Opened ${trade.action.toUpperCase()} ${trade.token || ""} ${trade.amount ? `(${trade.amount})` : ""}`);
1581
+ } else if (trade.action === "close") {
1582
+ lines.push(`${icon} Closed ${trade.token || ""} position${trade.pnl ? ` | PnL: ${trade.pnl}` : ""}`);
1583
+ } else {
1584
+ lines.push(`${icon} ${trade.action.toUpperCase()} ${trade.amount || ""} ${trade.token || ""}`);
1585
+ }
1586
+ if (trade.price) lines.push(`Price: $${trade.price}`);
1587
+ if (trade.hash) lines.push(`tx: ${trade.hash}`);
1588
+ if (trade.extra) lines.push(trade.extra);
1589
+ const content = lines.join("<br>");
1590
+ return this.createThread(content);
1591
+ }
1495
1592
  async request(method, path, body, query) {
1496
1593
  if (!this.arenaApiKey) throw new Error("Arena API key required for social endpoints. Pass arenaApiKey in config.");
1497
1594
  let url = `${ARENA_SOCIAL_API}${path}`;
@@ -1945,7 +2042,7 @@ var MarketModule = class {
1945
2042
  };
1946
2043
 
1947
2044
  // src/modules/defi.ts
1948
- import { ethers as ethers7 } from "ethers";
2045
+ import { ethers as ethers8 } from "ethers";
1949
2046
  var SAVAX_ADDRESS = "0x2b2C81e08f1Af8835a78Bb2A90AE924ACE0eA4bE";
1950
2047
  var SAVAX_ABI = [
1951
2048
  "function submit() payable returns (uint256)",
@@ -1986,7 +2083,7 @@ var ERC20_ABI2 = [
1986
2083
  var DefiModule = class {
1987
2084
  constructor(provider) {
1988
2085
  this.provider = provider;
1989
- this.savax = new ethers7.Contract(ethers7.getAddress(SAVAX_ADDRESS), SAVAX_ABI, provider);
2086
+ this.savax = new ethers8.Contract(ethers8.getAddress(SAVAX_ADDRESS), SAVAX_ABI, provider);
1990
2087
  }
1991
2088
  savax;
1992
2089
  // ── sAVAX Liquid Staking ──
@@ -1996,36 +2093,36 @@ var DefiModule = class {
1996
2093
  this.savax.totalPooledAvax(),
1997
2094
  this.savax.totalShares()
1998
2095
  ]);
1999
- const exchangeRate = totalShares > 0n ? ethers7.formatEther(totalPooled * ethers7.parseEther("1") / totalShares) : "1.0";
2096
+ const exchangeRate = totalShares > 0n ? ethers8.formatEther(totalPooled * ethers8.parseEther("1") / totalShares) : "1.0";
2000
2097
  const result = {
2001
2098
  exchangeRate,
2002
- totalPooledAvax: ethers7.formatEther(totalPooled),
2003
- totalShares: ethers7.formatEther(totalShares)
2099
+ totalPooledAvax: ethers8.formatEther(totalPooled),
2100
+ totalShares: ethers8.formatEther(totalShares)
2004
2101
  };
2005
2102
  if (wallet) {
2006
2103
  const balance = await this.savax.balanceOf(wallet);
2007
2104
  const avaxValue = await this.savax.getPooledAvaxByShares(balance);
2008
- result.balance = ethers7.formatEther(balance);
2009
- result.balanceInAvax = ethers7.formatEther(avaxValue);
2105
+ result.balance = ethers8.formatEther(balance);
2106
+ result.balanceInAvax = ethers8.formatEther(avaxValue);
2010
2107
  }
2011
2108
  return result;
2012
2109
  }
2013
2110
  /** Quote: how much sAVAX for staking AVAX */
2014
2111
  async sAvaxStakeQuote(avaxAmount) {
2015
- const avaxWei = ethers7.parseEther(avaxAmount);
2112
+ const avaxWei = ethers8.parseEther(avaxAmount);
2016
2113
  const shares = await this.savax.getSharesByPooledAvax(avaxWei);
2017
- return { avaxIn: avaxAmount, savaxOut: ethers7.formatEther(shares) };
2114
+ return { avaxIn: avaxAmount, savaxOut: ethers8.formatEther(shares) };
2018
2115
  }
2019
2116
  /** Build tx to stake AVAX → sAVAX */
2020
2117
  async buildSAvaxStake(avaxAmount) {
2021
- const avaxWei = ethers7.parseEther(avaxAmount);
2022
- const iface = new ethers7.Interface(SAVAX_ABI);
2118
+ const avaxWei = ethers8.parseEther(avaxAmount);
2119
+ const iface = new ethers8.Interface(SAVAX_ABI);
2023
2120
  const data = iface.encodeFunctionData("submit", []);
2024
2121
  return {
2025
2122
  transactions: [{
2026
- to: ethers7.getAddress(SAVAX_ADDRESS),
2123
+ to: ethers8.getAddress(SAVAX_ADDRESS),
2027
2124
  data,
2028
- value: ethers7.toBeHex(avaxWei, 32),
2125
+ value: ethers8.toBeHex(avaxWei, 32),
2029
2126
  chainId: CHAIN_ID,
2030
2127
  gasLimit: "300000",
2031
2128
  description: `Stake ${avaxAmount} AVAX \u2192 sAVAX`
@@ -2038,26 +2135,26 @@ var DefiModule = class {
2038
2135
  if (amount === "max") {
2039
2136
  shareAmount = await this.savax.balanceOf(wallet);
2040
2137
  } else {
2041
- shareAmount = ethers7.parseEther(amount);
2138
+ shareAmount = ethers8.parseEther(amount);
2042
2139
  }
2043
2140
  if (shareAmount === 0n) throw new Error("Zero sAVAX balance");
2044
- const iface = new ethers7.Interface(SAVAX_ABI);
2141
+ const iface = new ethers8.Interface(SAVAX_ABI);
2045
2142
  const data = iface.encodeFunctionData("requestUnlock", [shareAmount]);
2046
2143
  return {
2047
2144
  transactions: [{
2048
- to: ethers7.getAddress(SAVAX_ADDRESS),
2145
+ to: ethers8.getAddress(SAVAX_ADDRESS),
2049
2146
  data,
2050
2147
  value: "0",
2051
2148
  chainId: CHAIN_ID,
2052
2149
  gasLimit: "300000",
2053
- description: `Request unstake ${ethers7.formatEther(shareAmount)} sAVAX`
2150
+ description: `Request unstake ${ethers8.formatEther(shareAmount)} sAVAX`
2054
2151
  }]
2055
2152
  };
2056
2153
  }
2057
2154
  // ── ERC-4626 Vaults ──
2058
2155
  /** Get info about any ERC-4626 vault */
2059
2156
  async vaultInfo(vaultAddress, wallet) {
2060
- const vault = new ethers7.Contract(ethers7.getAddress(vaultAddress), VAULT_ABI, this.provider);
2157
+ const vault = new ethers8.Contract(ethers8.getAddress(vaultAddress), VAULT_ABI, this.provider);
2061
2158
  const [name, symbol, asset, totalAssets, totalSupply, decimals] = await Promise.all([
2062
2159
  vault.name(),
2063
2160
  vault.symbol(),
@@ -2066,70 +2163,70 @@ var DefiModule = class {
2066
2163
  vault.totalSupply(),
2067
2164
  vault.decimals()
2068
2165
  ]);
2069
- const sharePrice = totalSupply > 0n ? ethers7.formatUnits(totalAssets * 10n ** BigInt(decimals) / totalSupply, decimals) : "1.0";
2166
+ const sharePrice = totalSupply > 0n ? ethers8.formatUnits(totalAssets * 10n ** BigInt(decimals) / totalSupply, decimals) : "1.0";
2070
2167
  const result = {
2071
2168
  name,
2072
2169
  symbol,
2073
2170
  asset,
2074
- totalAssets: ethers7.formatUnits(totalAssets, decimals),
2075
- totalSupply: ethers7.formatUnits(totalSupply, decimals),
2171
+ totalAssets: ethers8.formatUnits(totalAssets, decimals),
2172
+ totalSupply: ethers8.formatUnits(totalSupply, decimals),
2076
2173
  sharePrice
2077
2174
  };
2078
2175
  if (wallet) {
2079
2176
  const shares = await vault.balanceOf(wallet);
2080
2177
  const assets = shares > 0n ? await vault.convertToAssets(shares) : 0n;
2081
- result.userShares = ethers7.formatUnits(shares, decimals);
2082
- result.userAssets = ethers7.formatUnits(assets, decimals);
2178
+ result.userShares = ethers8.formatUnits(shares, decimals);
2179
+ result.userAssets = ethers8.formatUnits(assets, decimals);
2083
2180
  }
2084
2181
  return result;
2085
2182
  }
2086
2183
  /** Quote vault deposit — how many shares for given assets */
2087
2184
  async vaultDepositQuote(vaultAddress, amount) {
2088
- const vault = new ethers7.Contract(ethers7.getAddress(vaultAddress), VAULT_ABI, this.provider);
2185
+ const vault = new ethers8.Contract(ethers8.getAddress(vaultAddress), VAULT_ABI, this.provider);
2089
2186
  const decimals = Number(await vault.decimals());
2090
- const assetsWei = ethers7.parseUnits(amount, decimals);
2187
+ const assetsWei = ethers8.parseUnits(amount, decimals);
2091
2188
  const shares = await vault.previewDeposit(assetsWei);
2092
- return { assetsIn: amount, sharesOut: ethers7.formatUnits(shares, decimals) };
2189
+ return { assetsIn: amount, sharesOut: ethers8.formatUnits(shares, decimals) };
2093
2190
  }
2094
2191
  /** Build txs to deposit into an ERC-4626 vault: [approve, deposit] */
2095
2192
  async buildVaultDeposit(wallet, vaultAddress, amount) {
2096
- const vaultAddr = ethers7.getAddress(vaultAddress);
2097
- const vault = new ethers7.Contract(vaultAddr, VAULT_ABI, this.provider);
2193
+ const vaultAddr = ethers8.getAddress(vaultAddress);
2194
+ const vault = new ethers8.Contract(vaultAddr, VAULT_ABI, this.provider);
2098
2195
  const assetAddr = await vault.asset();
2099
- const assetToken = new ethers7.Contract(assetAddr, ERC20_ABI2, this.provider);
2196
+ const assetToken = new ethers8.Contract(assetAddr, ERC20_ABI2, this.provider);
2100
2197
  const decimals = Number(await assetToken.decimals());
2101
2198
  let depositAmount;
2102
2199
  if (amount === "max") {
2103
2200
  depositAmount = await assetToken.balanceOf(wallet);
2104
2201
  } else {
2105
- depositAmount = ethers7.parseUnits(amount, decimals);
2202
+ depositAmount = ethers8.parseUnits(amount, decimals);
2106
2203
  }
2107
2204
  if (depositAmount === 0n) throw new Error("Zero balance to deposit");
2108
- const erc20Iface = new ethers7.Interface(ERC20_ABI2);
2205
+ const erc20Iface = new ethers8.Interface(ERC20_ABI2);
2109
2206
  const approveData = erc20Iface.encodeFunctionData("approve", [vaultAddr, depositAmount]);
2110
- const vaultIface = new ethers7.Interface(VAULT_ABI);
2207
+ const vaultIface = new ethers8.Interface(VAULT_ABI);
2111
2208
  const depositData = vaultIface.encodeFunctionData("deposit", [depositAmount, wallet]);
2112
2209
  return {
2113
2210
  transactions: [
2114
2211
  { to: assetAddr, data: approveData, value: "0", chainId: CHAIN_ID, gasLimit: "60000", description: `Approve asset for vault deposit` },
2115
- { to: vaultAddr, data: depositData, value: "0", chainId: CHAIN_ID, gasLimit: "300000", description: `Deposit ${ethers7.formatUnits(depositAmount, decimals)} into vault` }
2212
+ { to: vaultAddr, data: depositData, value: "0", chainId: CHAIN_ID, gasLimit: "300000", description: `Deposit ${ethers8.formatUnits(depositAmount, decimals)} into vault` }
2116
2213
  ]
2117
2214
  };
2118
2215
  }
2119
2216
  /** Build tx to withdraw from an ERC-4626 vault */
2120
2217
  async buildVaultWithdraw(wallet, vaultAddress, amount) {
2121
- const vaultAddr = ethers7.getAddress(vaultAddress);
2122
- const vault = new ethers7.Contract(vaultAddr, VAULT_ABI, this.provider);
2218
+ const vaultAddr = ethers8.getAddress(vaultAddress);
2219
+ const vault = new ethers8.Contract(vaultAddr, VAULT_ABI, this.provider);
2123
2220
  const decimals = Number(await vault.decimals());
2124
2221
  let withdrawAmount;
2125
2222
  if (amount === "max") {
2126
2223
  const shares = await vault.balanceOf(wallet);
2127
2224
  withdrawAmount = await vault.convertToAssets(shares);
2128
2225
  } else {
2129
- withdrawAmount = ethers7.parseUnits(amount, decimals);
2226
+ withdrawAmount = ethers8.parseUnits(amount, decimals);
2130
2227
  }
2131
2228
  if (withdrawAmount === 0n) throw new Error("Nothing to withdraw");
2132
- const iface = new ethers7.Interface(VAULT_ABI);
2229
+ const iface = new ethers8.Interface(VAULT_ABI);
2133
2230
  const data = iface.encodeFunctionData("withdraw", [withdrawAmount, wallet, wallet]);
2134
2231
  return {
2135
2232
  transactions: [{
@@ -2138,12 +2235,139 @@ var DefiModule = class {
2138
2235
  value: "0",
2139
2236
  chainId: CHAIN_ID,
2140
2237
  gasLimit: "300000",
2141
- description: `Withdraw ${ethers7.formatUnits(withdrawAmount, decimals)} from vault`
2238
+ description: `Withdraw ${ethers8.formatUnits(withdrawAmount, decimals)} from vault`
2142
2239
  }]
2143
2240
  };
2144
2241
  }
2145
2242
  };
2146
2243
 
2244
+ // src/modules/copytrading.ts
2245
+ var CopyTradingModule = class {
2246
+ perps;
2247
+ constructor(perps) {
2248
+ this.perps = perps;
2249
+ }
2250
+ /** Fetch all open positions for a Hyperliquid wallet */
2251
+ async getTargetPositions(targetWallet) {
2252
+ const state = await this.perps.getPositions(targetWallet);
2253
+ const assetPositions = state?.assetPositions ?? [];
2254
+ return assetPositions.map((ap) => ap.position).filter((p) => p && parseFloat(p.szi) !== 0);
2255
+ }
2256
+ /** Fetch your agent's current positions */
2257
+ async getAgentPositions(agentWallet) {
2258
+ const state = await this.perps.getPositions(agentWallet);
2259
+ const assetPositions = state?.assetPositions ?? [];
2260
+ return assetPositions.map((ap) => ap.position).filter((p) => p && parseFloat(p.szi) !== 0);
2261
+ }
2262
+ /**
2263
+ * Compare target wallet positions with agent positions and return orders needed to mirror.
2264
+ * Uses proportional sizing based on the scale factor.
2265
+ *
2266
+ * @param targetWallet - Wallet to copy from
2267
+ * @param agentWallet - Your agent's wallet
2268
+ * @param scaleFactor - Position scale (0.1 = 10% of target size, 1.0 = same size)
2269
+ */
2270
+ async calculateMirrorOrders(targetWallet, agentWallet, scaleFactor = 0.1) {
2271
+ const [targetPositions, agentPositions] = await Promise.all([
2272
+ this.getTargetPositions(targetWallet),
2273
+ this.getAgentPositions(agentWallet)
2274
+ ]);
2275
+ const agentMap = /* @__PURE__ */ new Map();
2276
+ for (const p of agentPositions) {
2277
+ agentMap.set(p.coin, p);
2278
+ }
2279
+ const targetMap = /* @__PURE__ */ new Map();
2280
+ for (const p of targetPositions) {
2281
+ targetMap.set(p.coin, p);
2282
+ }
2283
+ const orders = [];
2284
+ for (const [coin, targetPos] of targetMap) {
2285
+ const targetSize = parseFloat(targetPos.szi);
2286
+ const direction = targetSize > 0 ? "long" : "short";
2287
+ const desiredSize = Math.abs(targetSize) * scaleFactor;
2288
+ const agentPos = agentMap.get(coin);
2289
+ if (!agentPos) {
2290
+ orders.push({ symbol: coin, direction, size: desiredSize, action: "open", reason: `Mirror ${coin} ${direction} from target` });
2291
+ } else {
2292
+ const agentSize = parseFloat(agentPos.szi);
2293
+ const agentDir = agentSize > 0 ? "long" : "short";
2294
+ if (agentDir !== direction) {
2295
+ orders.push({ symbol: coin, direction: agentDir, size: Math.abs(agentSize), action: "close", reason: `Close ${agentDir} to flip to ${direction}` });
2296
+ orders.push({ symbol: coin, direction, size: desiredSize, action: "open", reason: `Mirror ${coin} ${direction} from target` });
2297
+ }
2298
+ }
2299
+ }
2300
+ for (const [coin, agentPos] of agentMap) {
2301
+ if (!targetMap.has(coin)) {
2302
+ const agentSize = parseFloat(agentPos.szi);
2303
+ const dir = agentSize > 0 ? "long" : "short";
2304
+ orders.push({ symbol: coin, direction: dir, size: Math.abs(agentSize), action: "close", reason: `Target closed ${coin} position` });
2305
+ }
2306
+ }
2307
+ return { orders, targetPositions, agentPositions };
2308
+ }
2309
+ /**
2310
+ * Execute mirror orders via the perps module (Arena API).
2311
+ * Returns results for each order.
2312
+ */
2313
+ async executeMirrorOrders(orders, currentPrices) {
2314
+ const results = [];
2315
+ for (const order of orders) {
2316
+ const price = currentPrices[order.symbol];
2317
+ if (!price) {
2318
+ results.push({ order, error: `No price for ${order.symbol}` });
2319
+ continue;
2320
+ }
2321
+ try {
2322
+ if (order.action === "close") {
2323
+ const result = await this.perps.closePosition(order.symbol, order.direction, order.size, price);
2324
+ results.push({ order, result });
2325
+ } else {
2326
+ const result = await this.perps.placeOrder([{
2327
+ symbol: order.symbol,
2328
+ direction: order.direction,
2329
+ orderType: "market",
2330
+ leverageType: "cross",
2331
+ size: order.size,
2332
+ leverage: 1
2333
+ }]);
2334
+ results.push({ order, result });
2335
+ }
2336
+ } catch (e) {
2337
+ results.push({ order, error: e.message });
2338
+ }
2339
+ }
2340
+ return results;
2341
+ }
2342
+ /**
2343
+ * One-shot copy: calculate + execute mirror orders in one call.
2344
+ * Fetches current prices from Hyperliquid for execution.
2345
+ */
2346
+ async copyOnce(targetWallet, agentWallet, scaleFactor = 0.1) {
2347
+ const { orders } = await this.calculateMirrorOrders(targetWallet, agentWallet, scaleFactor);
2348
+ if (orders.length === 0) return { orders, results: [] };
2349
+ const pairs = await this.perps.getTradingPairs();
2350
+ const midPrices = await this.fetchMidPrices(orders.map((o) => o.symbol), pairs.pairs);
2351
+ const results = await this.executeMirrorOrders(orders, midPrices);
2352
+ return { orders, results };
2353
+ }
2354
+ /** Fetch mid-market prices from Hyperliquid for given symbols */
2355
+ async fetchMidPrices(symbols, pairs) {
2356
+ const res = await fetch(HL_INFO, {
2357
+ method: "POST",
2358
+ headers: { "Content-Type": "application/json" },
2359
+ body: JSON.stringify({ type: "allMids" })
2360
+ });
2361
+ if (!res.ok) throw new Error(`Failed to fetch mid prices: ${res.status}`);
2362
+ const mids = await res.json();
2363
+ const prices = {};
2364
+ for (const sym of symbols) {
2365
+ if (mids[sym]) prices[sym] = parseFloat(mids[sym]);
2366
+ }
2367
+ return prices;
2368
+ }
2369
+ };
2370
+
2147
2371
  // src/client.ts
2148
2372
  var Logiqical = class _Logiqical {
2149
2373
  /** Buy/sell ARENA tokens via LFJ DEX */
@@ -2168,6 +2392,8 @@ var Logiqical = class _Logiqical {
2168
2392
  market;
2169
2393
  /** DeFi — sAVAX liquid staking + ERC-4626 vaults */
2170
2394
  defi;
2395
+ /** Copy trading — mirror Hyperliquid wallet positions */
2396
+ copyTrading;
2171
2397
  /** The agent's wallet address */
2172
2398
  address;
2173
2399
  /** The local wallet (null if read-only mode) */
@@ -2188,7 +2414,7 @@ var Logiqical = class _Logiqical {
2188
2414
  this.provider = this.agentWallet.provider;
2189
2415
  this.address = this.agentWallet.address;
2190
2416
  } else if (config.mnemonic) {
2191
- const hdWallet = ethers8.HDNodeWallet.fromPhrase(config.mnemonic);
2417
+ const hdWallet = ethers9.HDNodeWallet.fromPhrase(config.mnemonic);
2192
2418
  this.agentWallet = AgentWallet.fromPrivateKey(hdWallet.privateKey, {
2193
2419
  network: config.network,
2194
2420
  rpcUrl: config.rpcUrl
@@ -2214,6 +2440,7 @@ var Logiqical = class _Logiqical {
2214
2440
  this.signals = new SignalsModule();
2215
2441
  this.market = new MarketModule();
2216
2442
  this.defi = new DefiModule(this.provider);
2443
+ this.copyTrading = new CopyTradingModule(this.perps);
2217
2444
  }
2218
2445
  // ── Factory Methods ──
2219
2446
  /** Generate a brand new agent wallet */
@@ -2289,9 +2516,9 @@ var Logiqical = class _Logiqical {
2289
2516
  */
2290
2517
  async call(intent) {
2291
2518
  this.requireWallet("call");
2292
- const iface = new ethers8.Interface(intent.abi);
2519
+ const iface = new ethers9.Interface(intent.abi);
2293
2520
  const data = iface.encodeFunctionData(intent.method, intent.args ?? []);
2294
- const valueWei = intent.value ? ethers8.parseEther(intent.value).toString() : "0";
2521
+ const valueWei = intent.value ? ethers9.parseEther(intent.value).toString() : "0";
2295
2522
  const policyTx = { to: intent.contract, data, value: valueWei, chainId: 43114 };
2296
2523
  this.policyEngine.check(policyTx);
2297
2524
  if (this.policyEngine.shouldSimulate) {
@@ -2300,22 +2527,22 @@ var Logiqical = class _Logiqical {
2300
2527
  if (this.policyEngine.isDryRun) {
2301
2528
  return { hash: "0x_dry_run", receipt: { status: 1, blockNumber: 0, gasUsed: "0", transactionHash: "0x_dry_run" } };
2302
2529
  }
2303
- const contract = new Contract(
2304
- ethers8.getAddress(intent.contract),
2530
+ const contract = new Contract2(
2531
+ ethers9.getAddress(intent.contract),
2305
2532
  intent.abi,
2306
2533
  this.agentWallet.wallet
2307
2534
  );
2308
2535
  const tx = await contract[intent.method](
2309
2536
  ...intent.args ?? [],
2310
2537
  {
2311
- value: intent.value ? ethers8.parseEther(intent.value) : void 0,
2538
+ value: intent.value ? ethers9.parseEther(intent.value) : void 0,
2312
2539
  gasLimit: intent.gasLimit ?? void 0
2313
2540
  }
2314
2541
  );
2315
2542
  const receipt = await tx.wait(1);
2316
2543
  if (!receipt) throw new Error(`Transaction ${tx.hash} failed \u2014 no receipt.`);
2317
2544
  if (intent.value) {
2318
- this.policyEngine.recordSpend(ethers8.parseEther(intent.value));
2545
+ this.policyEngine.recordSpend(ethers9.parseEther(intent.value));
2319
2546
  }
2320
2547
  return {
2321
2548
  hash: tx.hash,
@@ -2330,7 +2557,7 @@ var Logiqical = class _Logiqical {
2330
2557
  /** Send native token (AVAX) — policy check + simulate + sign + broadcast */
2331
2558
  async send(to, amount) {
2332
2559
  this.requireWallet("send");
2333
- const valueWei = ethers8.parseEther(amount);
2560
+ const valueWei = ethers9.parseEther(amount);
2334
2561
  const policyTx = { to, data: "0x", value: valueWei.toString(), chainId: 43114 };
2335
2562
  this.policyEngine.check(policyTx);
2336
2563
  if (this.policyEngine.isDryRun) {
@@ -2397,7 +2624,7 @@ var Logiqical = class _Logiqical {
2397
2624
  async getBalance() {
2398
2625
  if (this.agentWallet) return this.agentWallet.getBalance();
2399
2626
  const balance = await this.provider.getBalance(this.address);
2400
- return ethers8.formatEther(balance);
2627
+ return ethers9.formatEther(balance);
2401
2628
  }
2402
2629
  /** Sign a message */
2403
2630
  async signMessage(message) {
@@ -2448,7 +2675,7 @@ var Logiqical = class _Logiqical {
2448
2675
  }
2449
2676
  const rpcUrl = config.rpcUrl ?? chain.rpcUrl;
2450
2677
  const chainId = chain?.chainId ?? void 0;
2451
- return { provider: new JsonRpcProvider2(rpcUrl, chainId) };
2678
+ return { provider: new JsonRpcProvider3(rpcUrl, chainId) };
2452
2679
  }
2453
2680
  };
2454
2681
 
@@ -3237,6 +3464,93 @@ function createMcpServer(agent) {
3237
3464
  return err(e);
3238
3465
  }
3239
3466
  });
3467
+ server.tool("agent_register", "Register a new AI agent on Arena (returns API key \u2014 save immediately)", {
3468
+ name: z.string().describe("Agent display name"),
3469
+ handle: z.string().describe("Unique agent handle"),
3470
+ address: z.string().describe("Agent wallet address"),
3471
+ bio: z.string().optional().describe("Agent bio"),
3472
+ profilePictureUrl: z.string().optional().describe("Profile picture URL"),
3473
+ bannerUrl: z.string().optional().describe("Banner image URL")
3474
+ }, async (opts) => {
3475
+ try {
3476
+ return ok(await SocialModule.registerAgent(opts));
3477
+ } catch (e) {
3478
+ return err(e);
3479
+ }
3480
+ });
3481
+ server.tool("social_post_trade", "Auto-post a trade update to Arena feed", {
3482
+ action: z.enum(["buy", "sell", "swap", "bridge", "stake", "long", "short", "close"]).describe("Trade action"),
3483
+ token: z.string().optional().describe("Token symbol"),
3484
+ amount: z.string().optional().describe("Amount traded"),
3485
+ price: z.string().optional().describe("Price in USD"),
3486
+ fromToken: z.string().optional().describe("Source token (for swaps)"),
3487
+ toToken: z.string().optional().describe("Destination token (for swaps)"),
3488
+ pnl: z.string().optional().describe("PnL for closed positions"),
3489
+ hash: z.string().optional().describe("Transaction hash"),
3490
+ extra: z.string().optional().describe("Additional info")
3491
+ }, async (trade) => {
3492
+ try {
3493
+ return ok(await agent.social.postTradeUpdate(trade));
3494
+ } catch (e) {
3495
+ return err(e);
3496
+ }
3497
+ });
3498
+ server.tool("copy_get_positions", "Get open positions of a Hyperliquid wallet (target for copy trading)", {
3499
+ wallet: z.string().describe("Target wallet address to copy")
3500
+ }, async ({ wallet }) => {
3501
+ try {
3502
+ return ok(await agent.copyTrading.getTargetPositions(wallet));
3503
+ } catch (e) {
3504
+ return err(e);
3505
+ }
3506
+ });
3507
+ server.tool("copy_calculate_orders", "Compare target vs agent positions and calculate mirror orders", {
3508
+ targetWallet: z.string().describe("Wallet to copy from"),
3509
+ agentWallet: z.string().optional().describe("Your agent wallet (defaults to agent address)"),
3510
+ scaleFactor: z.number().optional().describe("Position scale (0.1 = 10% of target size, default 0.1)")
3511
+ }, async ({ targetWallet, agentWallet, scaleFactor }) => {
3512
+ try {
3513
+ return ok(await agent.copyTrading.calculateMirrorOrders(targetWallet, agentWallet || w, scaleFactor));
3514
+ } catch (e) {
3515
+ return err(e);
3516
+ }
3517
+ });
3518
+ server.tool("copy_execute", "One-shot copy trade: mirror a wallet's positions (calculate + execute)", {
3519
+ targetWallet: z.string().describe("Wallet to copy from"),
3520
+ agentWallet: z.string().optional().describe("Your agent wallet (defaults to agent address)"),
3521
+ scaleFactor: z.number().optional().describe("Position scale (0.1 = 10% of target size, default 0.1)")
3522
+ }, async ({ targetWallet, agentWallet, scaleFactor }) => {
3523
+ try {
3524
+ return ok(await agent.copyTrading.copyOnce(targetWallet, agentWallet || w, scaleFactor));
3525
+ } catch (e) {
3526
+ return err(e);
3527
+ }
3528
+ });
3529
+ server.tool("perps_deposit_info", "Get Hyperliquid deposit info (addresses, chain, USDC)", {}, async () => {
3530
+ try {
3531
+ return ok(agent.perps.getDepositInfo());
3532
+ } catch (e) {
3533
+ return err(e);
3534
+ }
3535
+ });
3536
+ server.tool("perps_arbitrum_usdc_balance", "Check USDC balance on Arbitrum", {
3537
+ wallet: z.string().optional().describe("Wallet (defaults to agent)")
3538
+ }, async ({ wallet }) => {
3539
+ try {
3540
+ return ok({ balance: await agent.perps.getArbitrumUSDCBalance(wallet || w), token: "USDC", chain: "Arbitrum" });
3541
+ } catch (e) {
3542
+ return err(e);
3543
+ }
3544
+ });
3545
+ server.tool("perps_deposit_usdc", "Build USDC deposit tx for Hyperliquid (execute on Arbitrum network)", {
3546
+ amount: z.string().describe("USDC amount to deposit")
3547
+ }, async ({ amount }) => {
3548
+ try {
3549
+ return ok(agent.perps.buildDepositUSDC(amount));
3550
+ } catch (e) {
3551
+ return err(e);
3552
+ }
3553
+ });
3240
3554
  server.tool("call_contract", "Call any smart contract method \u2014 signs and broadcasts", {
3241
3555
  contract: z.string().describe("Contract address"),
3242
3556
  abi: z.array(z.string()).describe("Human-readable ABI array"),