logiqical 0.3.1 → 0.5.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/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/client.ts
2
- import { ethers as ethers8, JsonRpcProvider as JsonRpcProvider2, Contract } from "ethers";
2
+ import { ethers as ethers9, JsonRpcProvider as JsonRpcProvider3, Contract as Contract2 } from "ethers";
3
3
 
4
4
  // src/wallet.ts
5
5
  import {
@@ -440,6 +440,10 @@ var FRACTION_SCALER = 100;
440
440
  var ARENA_SOCIAL_API = "https://api.starsarena.com";
441
441
  var LIFI_API = "https://li.quest/v1";
442
442
  var HL_INFO = "https://api.hyperliquid.xyz/info";
443
+ var HL_DEPOSIT_ADDRESS = "0x2Df1c51E09aECF9cacB7bc98cB1742757f163dF7";
444
+ var USDC_ARBITRUM = "0xaf88d065e77c8cC2239327C5EDb3A432268e5831";
445
+ var ARBITRUM_CHAIN_ID = 42161;
446
+ var ARBITRUM_RPC = "https://arb1.arbitrum.io/rpc";
443
447
  var DEFAULT_SLIPPAGE_BPS = 500;
444
448
  var ERC20_ABI = [
445
449
  "function balanceOf(address owner) view returns (uint256)",
@@ -1136,6 +1140,7 @@ var DexModule = class {
1136
1140
  };
1137
1141
 
1138
1142
  // src/modules/perps.ts
1143
+ import { ethers as ethers6, JsonRpcProvider as JsonRpcProvider2, Contract } from "ethers";
1139
1144
  var PerpsModule = class {
1140
1145
  constructor(arenaApiKey) {
1141
1146
  this.arenaApiKey = arenaApiKey;
@@ -1229,6 +1234,51 @@ var PerpsModule = class {
1229
1234
  async getOpenOrders(wallet) {
1230
1235
  return this.hlPost({ type: "openOrders", user: wallet });
1231
1236
  }
1237
+ // ── USDC Deposit to Hyperliquid (on Arbitrum) ──
1238
+ /** Check USDC balance on Arbitrum */
1239
+ async getArbitrumUSDCBalance(wallet) {
1240
+ const provider = new JsonRpcProvider2(ARBITRUM_RPC, ARBITRUM_CHAIN_ID);
1241
+ const usdc = new Contract(USDC_ARBITRUM, ERC20_ABI, provider);
1242
+ const balance = await usdc.balanceOf(wallet);
1243
+ return ethers6.formatUnits(balance, 6);
1244
+ }
1245
+ /** Check ETH balance on Arbitrum (needed for gas) */
1246
+ async getArbitrumETHBalance(wallet) {
1247
+ const provider = new JsonRpcProvider2(ARBITRUM_RPC, ARBITRUM_CHAIN_ID);
1248
+ const balance = await provider.getBalance(wallet);
1249
+ return ethers6.formatEther(balance);
1250
+ }
1251
+ /**
1252
+ * Build a USDC deposit transaction for Hyperliquid on Arbitrum.
1253
+ * Returns an unsigned tx — use with agent.execute() after switching to Arbitrum network.
1254
+ *
1255
+ * Flow: agent.switchNetwork("arbitrum") → agent.execute(agent.perps.buildDepositUSDC(...))
1256
+ */
1257
+ buildDepositUSDC(amount) {
1258
+ const iface = new ethers6.Interface(["function transfer(address to, uint256 amount) returns (bool)"]);
1259
+ const amountWei = ethers6.parseUnits(amount, 6);
1260
+ const data = iface.encodeFunctionData("transfer", [HL_DEPOSIT_ADDRESS, amountWei]);
1261
+ return {
1262
+ transaction: {
1263
+ to: USDC_ARBITRUM,
1264
+ data,
1265
+ value: "0",
1266
+ chainId: ARBITRUM_CHAIN_ID,
1267
+ description: `Deposit ${amount} USDC to Hyperliquid`
1268
+ }
1269
+ };
1270
+ }
1271
+ /** Get deposit info (addresses + chain details) */
1272
+ getDepositInfo() {
1273
+ return {
1274
+ chain: "Arbitrum One",
1275
+ chainId: ARBITRUM_CHAIN_ID,
1276
+ usdcAddress: USDC_ARBITRUM,
1277
+ depositAddress: HL_DEPOSIT_ADDRESS,
1278
+ decimals: 6,
1279
+ tip: "Bridge USDC to Arbitrum first (use bridge module), then deposit to Hyperliquid"
1280
+ };
1281
+ }
1232
1282
  };
1233
1283
 
1234
1284
  // src/modules/bridge.ts
@@ -1385,37 +1435,37 @@ var BridgeModule = class {
1385
1435
  };
1386
1436
 
1387
1437
  // src/modules/tickets.ts
1388
- import { ethers as ethers6 } from "ethers";
1438
+ import { ethers as ethers7 } from "ethers";
1389
1439
  var TicketsModule = class {
1390
1440
  constructor(provider) {
1391
1441
  this.provider = provider;
1392
- this.contract = new ethers6.Contract(ethers6.getAddress(ARENA_SHARES_CONTRACT), SHARES_ABI, provider);
1442
+ this.contract = new ethers7.Contract(ethers7.getAddress(ARENA_SHARES_CONTRACT), SHARES_ABI, provider);
1393
1443
  }
1394
1444
  contract;
1395
1445
  async getBuyPrice(subject, amount = "1") {
1396
1446
  const fractionalAmount = this.ticketsToFractional(amount);
1397
- const subjectAddr = ethers6.getAddress(subject);
1447
+ const subjectAddr = ethers7.getAddress(subject);
1398
1448
  const [price, priceWithFee] = await Promise.all([
1399
1449
  this.contract.getBuyPriceForFractionalShares(subjectAddr, fractionalAmount),
1400
1450
  this.contract.getBuyPriceForFractionalSharesAfterFee(subjectAddr, fractionalAmount)
1401
1451
  ]);
1402
- return { price: ethers6.formatEther(price), priceWithFee: ethers6.formatEther(priceWithFee), tickets: amount, fractionalAmount };
1452
+ return { price: ethers7.formatEther(price), priceWithFee: ethers7.formatEther(priceWithFee), tickets: amount, fractionalAmount };
1403
1453
  }
1404
1454
  async getSellPrice(subject, amount = "1") {
1405
1455
  const fractionalAmount = this.ticketsToFractional(amount);
1406
- const subjectAddr = ethers6.getAddress(subject);
1456
+ const subjectAddr = ethers7.getAddress(subject);
1407
1457
  const [price, priceAfterFee] = await Promise.all([
1408
1458
  this.contract.getSellPriceForFractionalShares(subjectAddr, fractionalAmount),
1409
1459
  this.contract.getSellPriceForFractionalSharesAfterFee(subjectAddr, fractionalAmount)
1410
1460
  ]);
1411
- return { price: ethers6.formatEther(price), priceAfterFee: ethers6.formatEther(priceAfterFee), tickets: amount, fractionalAmount };
1461
+ return { price: ethers7.formatEther(price), priceAfterFee: ethers7.formatEther(priceAfterFee), tickets: amount, fractionalAmount };
1412
1462
  }
1413
1463
  async getBalance(subject, user) {
1414
- const frac = await this.contract.getMyFractionalShares(ethers6.getAddress(subject), ethers6.getAddress(user));
1464
+ const frac = await this.contract.getMyFractionalShares(ethers7.getAddress(subject), ethers7.getAddress(user));
1415
1465
  return { tickets: (Number(frac) / FRACTION_SCALER).toString(), fractionalAmount: frac.toString() };
1416
1466
  }
1417
1467
  async getSupply(subject) {
1418
- const subjectAddr = ethers6.getAddress(subject);
1468
+ const subjectAddr = ethers7.getAddress(subject);
1419
1469
  const [wholeSupply, fracSupply] = await Promise.all([
1420
1470
  this.contract.getSharesSupply(subjectAddr),
1421
1471
  this.contract.getTotalFractionalSupply(subjectAddr)
@@ -1433,42 +1483,42 @@ var TicketsModule = class {
1433
1483
  }
1434
1484
  async buildBuyTx(wallet, subject, amount = "1") {
1435
1485
  const fractionalAmount = this.ticketsToFractional(amount);
1436
- const subjectAddr = ethers6.getAddress(subject);
1437
- const userAddr = ethers6.getAddress(wallet);
1486
+ const subjectAddr = ethers7.getAddress(subject);
1487
+ const userAddr = ethers7.getAddress(wallet);
1438
1488
  const cost = await this.contract.getBuyPriceForFractionalSharesAfterFee(subjectAddr, fractionalAmount);
1439
1489
  if (cost === 0n) throw new Error("Could not get buy price \u2014 subject may not exist");
1440
- const iface = new ethers6.Interface(SHARES_ABI);
1490
+ const iface = new ethers7.Interface(SHARES_ABI);
1441
1491
  const data = iface.encodeFunctionData("buyFractionalShares", [subjectAddr, userAddr, fractionalAmount]);
1442
1492
  return {
1443
1493
  transaction: {
1444
- to: ethers6.getAddress(ARENA_SHARES_CONTRACT),
1494
+ to: ethers7.getAddress(ARENA_SHARES_CONTRACT),
1445
1495
  data,
1446
- value: ethers6.toBeHex(cost, 32),
1496
+ value: ethers7.toBeHex(cost, 32),
1447
1497
  chainId: CHAIN_ID,
1448
1498
  gasLimit: "200000",
1449
- description: `Buy ${amount} ticket(s) for ~${ethers6.formatEther(cost)} AVAX`
1499
+ description: `Buy ${amount} ticket(s) for ~${ethers7.formatEther(cost)} AVAX`
1450
1500
  }
1451
1501
  };
1452
1502
  }
1453
1503
  async buildSellTx(wallet, subject, amount = "1") {
1454
1504
  const fractionalAmount = this.ticketsToFractional(amount);
1455
- const subjectAddr = ethers6.getAddress(subject);
1456
- const userAddr = ethers6.getAddress(wallet);
1505
+ const subjectAddr = ethers7.getAddress(subject);
1506
+ const userAddr = ethers7.getAddress(wallet);
1457
1507
  const balance = await this.contract.getMyFractionalShares(subjectAddr, userAddr);
1458
1508
  if (balance < BigInt(fractionalAmount)) {
1459
1509
  throw new Error(`Insufficient tickets: have ${Number(balance) / FRACTION_SCALER}, trying to sell ${amount}`);
1460
1510
  }
1461
1511
  const proceeds = await this.contract.getSellPriceForFractionalSharesAfterFee(subjectAddr, fractionalAmount);
1462
- const iface = new ethers6.Interface(SHARES_ABI);
1512
+ const iface = new ethers7.Interface(SHARES_ABI);
1463
1513
  const data = iface.encodeFunctionData("sellFractionalShares", [subjectAddr, userAddr, fractionalAmount]);
1464
1514
  return {
1465
1515
  transaction: {
1466
- to: ethers6.getAddress(ARENA_SHARES_CONTRACT),
1516
+ to: ethers7.getAddress(ARENA_SHARES_CONTRACT),
1467
1517
  data,
1468
1518
  value: "0x0",
1469
1519
  chainId: CHAIN_ID,
1470
1520
  gasLimit: "200000",
1471
- description: `Sell ${amount} ticket(s) for ~${ethers6.formatEther(proceeds)} AVAX`
1521
+ description: `Sell ${amount} ticket(s) for ~${ethers7.formatEther(proceeds)} AVAX`
1472
1522
  }
1473
1523
  };
1474
1524
  }
@@ -1487,6 +1537,53 @@ var SocialModule = class {
1487
1537
  setArenaApiKey(key) {
1488
1538
  this.arenaApiKey = key;
1489
1539
  }
1540
+ // ── Agent Registration (no auth needed — returns API key) ──
1541
+ /**
1542
+ * Register a new AI agent on Arena. Returns API key (shown once — save immediately).
1543
+ * After registration, owner must claim by posting: "I'm claiming my AI Agent \"<name>\"\nVerification Code: <code>"
1544
+ */
1545
+ static async registerAgent(opts) {
1546
+ const res = await fetch(`${ARENA_SOCIAL_API}/agents/register`, {
1547
+ method: "POST",
1548
+ headers: { "Content-Type": "application/json" },
1549
+ body: JSON.stringify(opts)
1550
+ });
1551
+ const data = await res.json();
1552
+ if (!res.ok) throw new Error(data.message || data.error || `Registration failed (${res.status})`);
1553
+ return data;
1554
+ }
1555
+ // ── Feed Auto-Posting (trade updates) ──
1556
+ /** Post a formatted trade update to the Arena feed */
1557
+ async postTradeUpdate(trade) {
1558
+ const lines = [];
1559
+ const emoji = {
1560
+ buy: "\u{1F7E2}",
1561
+ sell: "\u{1F534}",
1562
+ swap: "\u{1F504}",
1563
+ bridge: "\u{1F309}",
1564
+ stake: "\u{1F512}",
1565
+ long: "\u{1F4C8}",
1566
+ short: "\u{1F4C9}",
1567
+ close: "\u2705"
1568
+ };
1569
+ const icon = emoji[trade.action] || "\u{1F4B0}";
1570
+ if (trade.action === "swap" && trade.fromToken && trade.toToken) {
1571
+ lines.push(`${icon} Swapped ${trade.amount || ""} ${trade.fromToken} \u2192 ${trade.toToken}`);
1572
+ } else if (trade.action === "bridge" && trade.fromToken) {
1573
+ lines.push(`${icon} Bridged ${trade.amount || ""} ${trade.fromToken}`);
1574
+ } else if (trade.action === "long" || trade.action === "short") {
1575
+ lines.push(`${icon} Opened ${trade.action.toUpperCase()} ${trade.token || ""} ${trade.amount ? `(${trade.amount})` : ""}`);
1576
+ } else if (trade.action === "close") {
1577
+ lines.push(`${icon} Closed ${trade.token || ""} position${trade.pnl ? ` | PnL: ${trade.pnl}` : ""}`);
1578
+ } else {
1579
+ lines.push(`${icon} ${trade.action.toUpperCase()} ${trade.amount || ""} ${trade.token || ""}`);
1580
+ }
1581
+ if (trade.price) lines.push(`Price: $${trade.price}`);
1582
+ if (trade.hash) lines.push(`tx: ${trade.hash}`);
1583
+ if (trade.extra) lines.push(trade.extra);
1584
+ const content = lines.join("<br>");
1585
+ return this.createThread(content);
1586
+ }
1490
1587
  async request(method, path, body, query) {
1491
1588
  if (!this.arenaApiKey) throw new Error("Arena API key required for social endpoints. Pass arenaApiKey in config.");
1492
1589
  let url = `${ARENA_SOCIAL_API}${path}`;
@@ -1940,7 +2037,7 @@ var MarketModule = class {
1940
2037
  };
1941
2038
 
1942
2039
  // src/modules/defi.ts
1943
- import { ethers as ethers7 } from "ethers";
2040
+ import { ethers as ethers8 } from "ethers";
1944
2041
  var SAVAX_ADDRESS = "0x2b2C81e08f1Af8835a78Bb2A90AE924ACE0eA4bE";
1945
2042
  var SAVAX_ABI = [
1946
2043
  "function submit() payable returns (uint256)",
@@ -1981,7 +2078,7 @@ var ERC20_ABI2 = [
1981
2078
  var DefiModule = class {
1982
2079
  constructor(provider) {
1983
2080
  this.provider = provider;
1984
- this.savax = new ethers7.Contract(ethers7.getAddress(SAVAX_ADDRESS), SAVAX_ABI, provider);
2081
+ this.savax = new ethers8.Contract(ethers8.getAddress(SAVAX_ADDRESS), SAVAX_ABI, provider);
1985
2082
  }
1986
2083
  savax;
1987
2084
  // ── sAVAX Liquid Staking ──
@@ -1991,36 +2088,36 @@ var DefiModule = class {
1991
2088
  this.savax.totalPooledAvax(),
1992
2089
  this.savax.totalShares()
1993
2090
  ]);
1994
- const exchangeRate = totalShares > 0n ? ethers7.formatEther(totalPooled * ethers7.parseEther("1") / totalShares) : "1.0";
2091
+ const exchangeRate = totalShares > 0n ? ethers8.formatEther(totalPooled * ethers8.parseEther("1") / totalShares) : "1.0";
1995
2092
  const result = {
1996
2093
  exchangeRate,
1997
- totalPooledAvax: ethers7.formatEther(totalPooled),
1998
- totalShares: ethers7.formatEther(totalShares)
2094
+ totalPooledAvax: ethers8.formatEther(totalPooled),
2095
+ totalShares: ethers8.formatEther(totalShares)
1999
2096
  };
2000
2097
  if (wallet) {
2001
2098
  const balance = await this.savax.balanceOf(wallet);
2002
2099
  const avaxValue = await this.savax.getPooledAvaxByShares(balance);
2003
- result.balance = ethers7.formatEther(balance);
2004
- result.balanceInAvax = ethers7.formatEther(avaxValue);
2100
+ result.balance = ethers8.formatEther(balance);
2101
+ result.balanceInAvax = ethers8.formatEther(avaxValue);
2005
2102
  }
2006
2103
  return result;
2007
2104
  }
2008
2105
  /** Quote: how much sAVAX for staking AVAX */
2009
2106
  async sAvaxStakeQuote(avaxAmount) {
2010
- const avaxWei = ethers7.parseEther(avaxAmount);
2107
+ const avaxWei = ethers8.parseEther(avaxAmount);
2011
2108
  const shares = await this.savax.getSharesByPooledAvax(avaxWei);
2012
- return { avaxIn: avaxAmount, savaxOut: ethers7.formatEther(shares) };
2109
+ return { avaxIn: avaxAmount, savaxOut: ethers8.formatEther(shares) };
2013
2110
  }
2014
2111
  /** Build tx to stake AVAX → sAVAX */
2015
2112
  async buildSAvaxStake(avaxAmount) {
2016
- const avaxWei = ethers7.parseEther(avaxAmount);
2017
- const iface = new ethers7.Interface(SAVAX_ABI);
2113
+ const avaxWei = ethers8.parseEther(avaxAmount);
2114
+ const iface = new ethers8.Interface(SAVAX_ABI);
2018
2115
  const data = iface.encodeFunctionData("submit", []);
2019
2116
  return {
2020
2117
  transactions: [{
2021
- to: ethers7.getAddress(SAVAX_ADDRESS),
2118
+ to: ethers8.getAddress(SAVAX_ADDRESS),
2022
2119
  data,
2023
- value: ethers7.toBeHex(avaxWei, 32),
2120
+ value: ethers8.toBeHex(avaxWei, 32),
2024
2121
  chainId: CHAIN_ID,
2025
2122
  gasLimit: "300000",
2026
2123
  description: `Stake ${avaxAmount} AVAX \u2192 sAVAX`
@@ -2033,26 +2130,26 @@ var DefiModule = class {
2033
2130
  if (amount === "max") {
2034
2131
  shareAmount = await this.savax.balanceOf(wallet);
2035
2132
  } else {
2036
- shareAmount = ethers7.parseEther(amount);
2133
+ shareAmount = ethers8.parseEther(amount);
2037
2134
  }
2038
2135
  if (shareAmount === 0n) throw new Error("Zero sAVAX balance");
2039
- const iface = new ethers7.Interface(SAVAX_ABI);
2136
+ const iface = new ethers8.Interface(SAVAX_ABI);
2040
2137
  const data = iface.encodeFunctionData("requestUnlock", [shareAmount]);
2041
2138
  return {
2042
2139
  transactions: [{
2043
- to: ethers7.getAddress(SAVAX_ADDRESS),
2140
+ to: ethers8.getAddress(SAVAX_ADDRESS),
2044
2141
  data,
2045
2142
  value: "0",
2046
2143
  chainId: CHAIN_ID,
2047
2144
  gasLimit: "300000",
2048
- description: `Request unstake ${ethers7.formatEther(shareAmount)} sAVAX`
2145
+ description: `Request unstake ${ethers8.formatEther(shareAmount)} sAVAX`
2049
2146
  }]
2050
2147
  };
2051
2148
  }
2052
2149
  // ── ERC-4626 Vaults ──
2053
2150
  /** Get info about any ERC-4626 vault */
2054
2151
  async vaultInfo(vaultAddress, wallet) {
2055
- const vault = new ethers7.Contract(ethers7.getAddress(vaultAddress), VAULT_ABI, this.provider);
2152
+ const vault = new ethers8.Contract(ethers8.getAddress(vaultAddress), VAULT_ABI, this.provider);
2056
2153
  const [name, symbol, asset, totalAssets, totalSupply, decimals] = await Promise.all([
2057
2154
  vault.name(),
2058
2155
  vault.symbol(),
@@ -2061,70 +2158,70 @@ var DefiModule = class {
2061
2158
  vault.totalSupply(),
2062
2159
  vault.decimals()
2063
2160
  ]);
2064
- const sharePrice = totalSupply > 0n ? ethers7.formatUnits(totalAssets * 10n ** BigInt(decimals) / totalSupply, decimals) : "1.0";
2161
+ const sharePrice = totalSupply > 0n ? ethers8.formatUnits(totalAssets * 10n ** BigInt(decimals) / totalSupply, decimals) : "1.0";
2065
2162
  const result = {
2066
2163
  name,
2067
2164
  symbol,
2068
2165
  asset,
2069
- totalAssets: ethers7.formatUnits(totalAssets, decimals),
2070
- totalSupply: ethers7.formatUnits(totalSupply, decimals),
2166
+ totalAssets: ethers8.formatUnits(totalAssets, decimals),
2167
+ totalSupply: ethers8.formatUnits(totalSupply, decimals),
2071
2168
  sharePrice
2072
2169
  };
2073
2170
  if (wallet) {
2074
2171
  const shares = await vault.balanceOf(wallet);
2075
2172
  const assets = shares > 0n ? await vault.convertToAssets(shares) : 0n;
2076
- result.userShares = ethers7.formatUnits(shares, decimals);
2077
- result.userAssets = ethers7.formatUnits(assets, decimals);
2173
+ result.userShares = ethers8.formatUnits(shares, decimals);
2174
+ result.userAssets = ethers8.formatUnits(assets, decimals);
2078
2175
  }
2079
2176
  return result;
2080
2177
  }
2081
2178
  /** Quote vault deposit — how many shares for given assets */
2082
2179
  async vaultDepositQuote(vaultAddress, amount) {
2083
- const vault = new ethers7.Contract(ethers7.getAddress(vaultAddress), VAULT_ABI, this.provider);
2180
+ const vault = new ethers8.Contract(ethers8.getAddress(vaultAddress), VAULT_ABI, this.provider);
2084
2181
  const decimals = Number(await vault.decimals());
2085
- const assetsWei = ethers7.parseUnits(amount, decimals);
2182
+ const assetsWei = ethers8.parseUnits(amount, decimals);
2086
2183
  const shares = await vault.previewDeposit(assetsWei);
2087
- return { assetsIn: amount, sharesOut: ethers7.formatUnits(shares, decimals) };
2184
+ return { assetsIn: amount, sharesOut: ethers8.formatUnits(shares, decimals) };
2088
2185
  }
2089
2186
  /** Build txs to deposit into an ERC-4626 vault: [approve, deposit] */
2090
2187
  async buildVaultDeposit(wallet, vaultAddress, amount) {
2091
- const vaultAddr = ethers7.getAddress(vaultAddress);
2092
- const vault = new ethers7.Contract(vaultAddr, VAULT_ABI, this.provider);
2188
+ const vaultAddr = ethers8.getAddress(vaultAddress);
2189
+ const vault = new ethers8.Contract(vaultAddr, VAULT_ABI, this.provider);
2093
2190
  const assetAddr = await vault.asset();
2094
- const assetToken = new ethers7.Contract(assetAddr, ERC20_ABI2, this.provider);
2191
+ const assetToken = new ethers8.Contract(assetAddr, ERC20_ABI2, this.provider);
2095
2192
  const decimals = Number(await assetToken.decimals());
2096
2193
  let depositAmount;
2097
2194
  if (amount === "max") {
2098
2195
  depositAmount = await assetToken.balanceOf(wallet);
2099
2196
  } else {
2100
- depositAmount = ethers7.parseUnits(amount, decimals);
2197
+ depositAmount = ethers8.parseUnits(amount, decimals);
2101
2198
  }
2102
2199
  if (depositAmount === 0n) throw new Error("Zero balance to deposit");
2103
- const erc20Iface = new ethers7.Interface(ERC20_ABI2);
2200
+ const erc20Iface = new ethers8.Interface(ERC20_ABI2);
2104
2201
  const approveData = erc20Iface.encodeFunctionData("approve", [vaultAddr, depositAmount]);
2105
- const vaultIface = new ethers7.Interface(VAULT_ABI);
2202
+ const vaultIface = new ethers8.Interface(VAULT_ABI);
2106
2203
  const depositData = vaultIface.encodeFunctionData("deposit", [depositAmount, wallet]);
2107
2204
  return {
2108
2205
  transactions: [
2109
2206
  { to: assetAddr, data: approveData, value: "0", chainId: CHAIN_ID, gasLimit: "60000", description: `Approve asset for vault deposit` },
2110
- { to: vaultAddr, data: depositData, value: "0", chainId: CHAIN_ID, gasLimit: "300000", description: `Deposit ${ethers7.formatUnits(depositAmount, decimals)} into vault` }
2207
+ { to: vaultAddr, data: depositData, value: "0", chainId: CHAIN_ID, gasLimit: "300000", description: `Deposit ${ethers8.formatUnits(depositAmount, decimals)} into vault` }
2111
2208
  ]
2112
2209
  };
2113
2210
  }
2114
2211
  /** Build tx to withdraw from an ERC-4626 vault */
2115
2212
  async buildVaultWithdraw(wallet, vaultAddress, amount) {
2116
- const vaultAddr = ethers7.getAddress(vaultAddress);
2117
- const vault = new ethers7.Contract(vaultAddr, VAULT_ABI, this.provider);
2213
+ const vaultAddr = ethers8.getAddress(vaultAddress);
2214
+ const vault = new ethers8.Contract(vaultAddr, VAULT_ABI, this.provider);
2118
2215
  const decimals = Number(await vault.decimals());
2119
2216
  let withdrawAmount;
2120
2217
  if (amount === "max") {
2121
2218
  const shares = await vault.balanceOf(wallet);
2122
2219
  withdrawAmount = await vault.convertToAssets(shares);
2123
2220
  } else {
2124
- withdrawAmount = ethers7.parseUnits(amount, decimals);
2221
+ withdrawAmount = ethers8.parseUnits(amount, decimals);
2125
2222
  }
2126
2223
  if (withdrawAmount === 0n) throw new Error("Nothing to withdraw");
2127
- const iface = new ethers7.Interface(VAULT_ABI);
2224
+ const iface = new ethers8.Interface(VAULT_ABI);
2128
2225
  const data = iface.encodeFunctionData("withdraw", [withdrawAmount, wallet, wallet]);
2129
2226
  return {
2130
2227
  transactions: [{
@@ -2133,12 +2230,139 @@ var DefiModule = class {
2133
2230
  value: "0",
2134
2231
  chainId: CHAIN_ID,
2135
2232
  gasLimit: "300000",
2136
- description: `Withdraw ${ethers7.formatUnits(withdrawAmount, decimals)} from vault`
2233
+ description: `Withdraw ${ethers8.formatUnits(withdrawAmount, decimals)} from vault`
2137
2234
  }]
2138
2235
  };
2139
2236
  }
2140
2237
  };
2141
2238
 
2239
+ // src/modules/copytrading.ts
2240
+ var CopyTradingModule = class {
2241
+ perps;
2242
+ constructor(perps) {
2243
+ this.perps = perps;
2244
+ }
2245
+ /** Fetch all open positions for a Hyperliquid wallet */
2246
+ async getTargetPositions(targetWallet) {
2247
+ const state = await this.perps.getPositions(targetWallet);
2248
+ const assetPositions = state?.assetPositions ?? [];
2249
+ return assetPositions.map((ap) => ap.position).filter((p) => p && parseFloat(p.szi) !== 0);
2250
+ }
2251
+ /** Fetch your agent's current positions */
2252
+ async getAgentPositions(agentWallet) {
2253
+ const state = await this.perps.getPositions(agentWallet);
2254
+ const assetPositions = state?.assetPositions ?? [];
2255
+ return assetPositions.map((ap) => ap.position).filter((p) => p && parseFloat(p.szi) !== 0);
2256
+ }
2257
+ /**
2258
+ * Compare target wallet positions with agent positions and return orders needed to mirror.
2259
+ * Uses proportional sizing based on the scale factor.
2260
+ *
2261
+ * @param targetWallet - Wallet to copy from
2262
+ * @param agentWallet - Your agent's wallet
2263
+ * @param scaleFactor - Position scale (0.1 = 10% of target size, 1.0 = same size)
2264
+ */
2265
+ async calculateMirrorOrders(targetWallet, agentWallet, scaleFactor = 0.1) {
2266
+ const [targetPositions, agentPositions] = await Promise.all([
2267
+ this.getTargetPositions(targetWallet),
2268
+ this.getAgentPositions(agentWallet)
2269
+ ]);
2270
+ const agentMap = /* @__PURE__ */ new Map();
2271
+ for (const p of agentPositions) {
2272
+ agentMap.set(p.coin, p);
2273
+ }
2274
+ const targetMap = /* @__PURE__ */ new Map();
2275
+ for (const p of targetPositions) {
2276
+ targetMap.set(p.coin, p);
2277
+ }
2278
+ const orders = [];
2279
+ for (const [coin, targetPos] of targetMap) {
2280
+ const targetSize = parseFloat(targetPos.szi);
2281
+ const direction = targetSize > 0 ? "long" : "short";
2282
+ const desiredSize = Math.abs(targetSize) * scaleFactor;
2283
+ const agentPos = agentMap.get(coin);
2284
+ if (!agentPos) {
2285
+ orders.push({ symbol: coin, direction, size: desiredSize, action: "open", reason: `Mirror ${coin} ${direction} from target` });
2286
+ } else {
2287
+ const agentSize = parseFloat(agentPos.szi);
2288
+ const agentDir = agentSize > 0 ? "long" : "short";
2289
+ if (agentDir !== direction) {
2290
+ orders.push({ symbol: coin, direction: agentDir, size: Math.abs(agentSize), action: "close", reason: `Close ${agentDir} to flip to ${direction}` });
2291
+ orders.push({ symbol: coin, direction, size: desiredSize, action: "open", reason: `Mirror ${coin} ${direction} from target` });
2292
+ }
2293
+ }
2294
+ }
2295
+ for (const [coin, agentPos] of agentMap) {
2296
+ if (!targetMap.has(coin)) {
2297
+ const agentSize = parseFloat(agentPos.szi);
2298
+ const dir = agentSize > 0 ? "long" : "short";
2299
+ orders.push({ symbol: coin, direction: dir, size: Math.abs(agentSize), action: "close", reason: `Target closed ${coin} position` });
2300
+ }
2301
+ }
2302
+ return { orders, targetPositions, agentPositions };
2303
+ }
2304
+ /**
2305
+ * Execute mirror orders via the perps module (Arena API).
2306
+ * Returns results for each order.
2307
+ */
2308
+ async executeMirrorOrders(orders, currentPrices) {
2309
+ const results = [];
2310
+ for (const order of orders) {
2311
+ const price = currentPrices[order.symbol];
2312
+ if (!price) {
2313
+ results.push({ order, error: `No price for ${order.symbol}` });
2314
+ continue;
2315
+ }
2316
+ try {
2317
+ if (order.action === "close") {
2318
+ const result = await this.perps.closePosition(order.symbol, order.direction, order.size, price);
2319
+ results.push({ order, result });
2320
+ } else {
2321
+ const result = await this.perps.placeOrder([{
2322
+ symbol: order.symbol,
2323
+ direction: order.direction,
2324
+ orderType: "market",
2325
+ leverageType: "cross",
2326
+ size: order.size,
2327
+ leverage: 1
2328
+ }]);
2329
+ results.push({ order, result });
2330
+ }
2331
+ } catch (e) {
2332
+ results.push({ order, error: e.message });
2333
+ }
2334
+ }
2335
+ return results;
2336
+ }
2337
+ /**
2338
+ * One-shot copy: calculate + execute mirror orders in one call.
2339
+ * Fetches current prices from Hyperliquid for execution.
2340
+ */
2341
+ async copyOnce(targetWallet, agentWallet, scaleFactor = 0.1) {
2342
+ const { orders } = await this.calculateMirrorOrders(targetWallet, agentWallet, scaleFactor);
2343
+ if (orders.length === 0) return { orders, results: [] };
2344
+ const pairs = await this.perps.getTradingPairs();
2345
+ const midPrices = await this.fetchMidPrices(orders.map((o) => o.symbol), pairs.pairs);
2346
+ const results = await this.executeMirrorOrders(orders, midPrices);
2347
+ return { orders, results };
2348
+ }
2349
+ /** Fetch mid-market prices from Hyperliquid for given symbols */
2350
+ async fetchMidPrices(symbols, pairs) {
2351
+ const res = await fetch(HL_INFO, {
2352
+ method: "POST",
2353
+ headers: { "Content-Type": "application/json" },
2354
+ body: JSON.stringify({ type: "allMids" })
2355
+ });
2356
+ if (!res.ok) throw new Error(`Failed to fetch mid prices: ${res.status}`);
2357
+ const mids = await res.json();
2358
+ const prices = {};
2359
+ for (const sym of symbols) {
2360
+ if (mids[sym]) prices[sym] = parseFloat(mids[sym]);
2361
+ }
2362
+ return prices;
2363
+ }
2364
+ };
2365
+
2142
2366
  // src/client.ts
2143
2367
  var Logiqical = class _Logiqical {
2144
2368
  /** Buy/sell ARENA tokens via LFJ DEX */
@@ -2163,6 +2387,8 @@ var Logiqical = class _Logiqical {
2163
2387
  market;
2164
2388
  /** DeFi — sAVAX liquid staking + ERC-4626 vaults */
2165
2389
  defi;
2390
+ /** Copy trading — mirror Hyperliquid wallet positions */
2391
+ copyTrading;
2166
2392
  /** The agent's wallet address */
2167
2393
  address;
2168
2394
  /** The local wallet (null if read-only mode) */
@@ -2183,7 +2409,7 @@ var Logiqical = class _Logiqical {
2183
2409
  this.provider = this.agentWallet.provider;
2184
2410
  this.address = this.agentWallet.address;
2185
2411
  } else if (config.mnemonic) {
2186
- const hdWallet = ethers8.HDNodeWallet.fromPhrase(config.mnemonic);
2412
+ const hdWallet = ethers9.HDNodeWallet.fromPhrase(config.mnemonic);
2187
2413
  this.agentWallet = AgentWallet.fromPrivateKey(hdWallet.privateKey, {
2188
2414
  network: config.network,
2189
2415
  rpcUrl: config.rpcUrl
@@ -2209,6 +2435,7 @@ var Logiqical = class _Logiqical {
2209
2435
  this.signals = new SignalsModule();
2210
2436
  this.market = new MarketModule();
2211
2437
  this.defi = new DefiModule(this.provider);
2438
+ this.copyTrading = new CopyTradingModule(this.perps);
2212
2439
  }
2213
2440
  // ── Factory Methods ──
2214
2441
  /** Generate a brand new agent wallet */
@@ -2284,9 +2511,9 @@ var Logiqical = class _Logiqical {
2284
2511
  */
2285
2512
  async call(intent) {
2286
2513
  this.requireWallet("call");
2287
- const iface = new ethers8.Interface(intent.abi);
2514
+ const iface = new ethers9.Interface(intent.abi);
2288
2515
  const data = iface.encodeFunctionData(intent.method, intent.args ?? []);
2289
- const valueWei = intent.value ? ethers8.parseEther(intent.value).toString() : "0";
2516
+ const valueWei = intent.value ? ethers9.parseEther(intent.value).toString() : "0";
2290
2517
  const policyTx = { to: intent.contract, data, value: valueWei, chainId: 43114 };
2291
2518
  this.policyEngine.check(policyTx);
2292
2519
  if (this.policyEngine.shouldSimulate) {
@@ -2295,22 +2522,22 @@ var Logiqical = class _Logiqical {
2295
2522
  if (this.policyEngine.isDryRun) {
2296
2523
  return { hash: "0x_dry_run", receipt: { status: 1, blockNumber: 0, gasUsed: "0", transactionHash: "0x_dry_run" } };
2297
2524
  }
2298
- const contract = new Contract(
2299
- ethers8.getAddress(intent.contract),
2525
+ const contract = new Contract2(
2526
+ ethers9.getAddress(intent.contract),
2300
2527
  intent.abi,
2301
2528
  this.agentWallet.wallet
2302
2529
  );
2303
2530
  const tx = await contract[intent.method](
2304
2531
  ...intent.args ?? [],
2305
2532
  {
2306
- value: intent.value ? ethers8.parseEther(intent.value) : void 0,
2533
+ value: intent.value ? ethers9.parseEther(intent.value) : void 0,
2307
2534
  gasLimit: intent.gasLimit ?? void 0
2308
2535
  }
2309
2536
  );
2310
2537
  const receipt = await tx.wait(1);
2311
2538
  if (!receipt) throw new Error(`Transaction ${tx.hash} failed \u2014 no receipt.`);
2312
2539
  if (intent.value) {
2313
- this.policyEngine.recordSpend(ethers8.parseEther(intent.value));
2540
+ this.policyEngine.recordSpend(ethers9.parseEther(intent.value));
2314
2541
  }
2315
2542
  return {
2316
2543
  hash: tx.hash,
@@ -2325,7 +2552,7 @@ var Logiqical = class _Logiqical {
2325
2552
  /** Send native token (AVAX) — policy check + simulate + sign + broadcast */
2326
2553
  async send(to, amount) {
2327
2554
  this.requireWallet("send");
2328
- const valueWei = ethers8.parseEther(amount);
2555
+ const valueWei = ethers9.parseEther(amount);
2329
2556
  const policyTx = { to, data: "0x", value: valueWei.toString(), chainId: 43114 };
2330
2557
  this.policyEngine.check(policyTx);
2331
2558
  if (this.policyEngine.isDryRun) {
@@ -2392,7 +2619,7 @@ var Logiqical = class _Logiqical {
2392
2619
  async getBalance() {
2393
2620
  if (this.agentWallet) return this.agentWallet.getBalance();
2394
2621
  const balance = await this.provider.getBalance(this.address);
2395
- return ethers8.formatEther(balance);
2622
+ return ethers9.formatEther(balance);
2396
2623
  }
2397
2624
  /** Sign a message */
2398
2625
  async signMessage(message) {
@@ -2443,7 +2670,7 @@ var Logiqical = class _Logiqical {
2443
2670
  }
2444
2671
  const rpcUrl = config.rpcUrl ?? chain.rpcUrl;
2445
2672
  const chainId = chain?.chainId ?? void 0;
2446
- return { provider: new JsonRpcProvider2(rpcUrl, chainId) };
2673
+ return { provider: new JsonRpcProvider3(rpcUrl, chainId) };
2447
2674
  }
2448
2675
  };
2449
2676
  var LogiqicalClient = Logiqical;
@@ -2466,6 +2693,7 @@ export {
2466
2693
  AgentWallet,
2467
2694
  BridgeModule,
2468
2695
  CHAINS,
2696
+ CopyTradingModule,
2469
2697
  DefiModule,
2470
2698
  DexModule,
2471
2699
  LaunchpadModule,