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 +382 -68
- package/dist/index.d.mts +120 -1
- package/dist/index.d.ts +120 -1
- package/dist/index.js +297 -68
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +296 -68
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -4
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
|
|
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
|
|
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
|
|
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 =
|
|
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:
|
|
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 =
|
|
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:
|
|
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(
|
|
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 =
|
|
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 =
|
|
1442
|
-
const userAddr =
|
|
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
|
|
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:
|
|
1499
|
+
to: ethers7.getAddress(ARENA_SHARES_CONTRACT),
|
|
1450
1500
|
data,
|
|
1451
|
-
value:
|
|
1501
|
+
value: ethers7.toBeHex(cost, 32),
|
|
1452
1502
|
chainId: CHAIN_ID,
|
|
1453
1503
|
gasLimit: "200000",
|
|
1454
|
-
description: `Buy ${amount} ticket(s) for ~${
|
|
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 =
|
|
1461
|
-
const userAddr =
|
|
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
|
|
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:
|
|
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 ~${
|
|
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
|
|
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
|
|
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 ?
|
|
2096
|
+
const exchangeRate = totalShares > 0n ? ethers8.formatEther(totalPooled * ethers8.parseEther("1") / totalShares) : "1.0";
|
|
2000
2097
|
const result = {
|
|
2001
2098
|
exchangeRate,
|
|
2002
|
-
totalPooledAvax:
|
|
2003
|
-
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 =
|
|
2009
|
-
result.balanceInAvax =
|
|
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 =
|
|
2112
|
+
const avaxWei = ethers8.parseEther(avaxAmount);
|
|
2016
2113
|
const shares = await this.savax.getSharesByPooledAvax(avaxWei);
|
|
2017
|
-
return { avaxIn: avaxAmount, savaxOut:
|
|
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 =
|
|
2022
|
-
const iface = new
|
|
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:
|
|
2123
|
+
to: ethers8.getAddress(SAVAX_ADDRESS),
|
|
2027
2124
|
data,
|
|
2028
|
-
value:
|
|
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 =
|
|
2138
|
+
shareAmount = ethers8.parseEther(amount);
|
|
2042
2139
|
}
|
|
2043
2140
|
if (shareAmount === 0n) throw new Error("Zero sAVAX balance");
|
|
2044
|
-
const iface = new
|
|
2141
|
+
const iface = new ethers8.Interface(SAVAX_ABI);
|
|
2045
2142
|
const data = iface.encodeFunctionData("requestUnlock", [shareAmount]);
|
|
2046
2143
|
return {
|
|
2047
2144
|
transactions: [{
|
|
2048
|
-
to:
|
|
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 ${
|
|
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
|
|
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 ?
|
|
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:
|
|
2075
|
-
totalSupply:
|
|
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 =
|
|
2082
|
-
result.userAssets =
|
|
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
|
|
2185
|
+
const vault = new ethers8.Contract(ethers8.getAddress(vaultAddress), VAULT_ABI, this.provider);
|
|
2089
2186
|
const decimals = Number(await vault.decimals());
|
|
2090
|
-
const assetsWei =
|
|
2187
|
+
const assetsWei = ethers8.parseUnits(amount, decimals);
|
|
2091
2188
|
const shares = await vault.previewDeposit(assetsWei);
|
|
2092
|
-
return { assetsIn: amount, sharesOut:
|
|
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 =
|
|
2097
|
-
const vault = new
|
|
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
|
|
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 =
|
|
2202
|
+
depositAmount = ethers8.parseUnits(amount, decimals);
|
|
2106
2203
|
}
|
|
2107
2204
|
if (depositAmount === 0n) throw new Error("Zero balance to deposit");
|
|
2108
|
-
const erc20Iface = new
|
|
2205
|
+
const erc20Iface = new ethers8.Interface(ERC20_ABI2);
|
|
2109
2206
|
const approveData = erc20Iface.encodeFunctionData("approve", [vaultAddr, depositAmount]);
|
|
2110
|
-
const vaultIface = new
|
|
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 ${
|
|
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 =
|
|
2122
|
-
const vault = new
|
|
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 =
|
|
2226
|
+
withdrawAmount = ethers8.parseUnits(amount, decimals);
|
|
2130
2227
|
}
|
|
2131
2228
|
if (withdrawAmount === 0n) throw new Error("Nothing to withdraw");
|
|
2132
|
-
const iface = new
|
|
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 ${
|
|
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 =
|
|
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
|
|
2519
|
+
const iface = new ethers9.Interface(intent.abi);
|
|
2293
2520
|
const data = iface.encodeFunctionData(intent.method, intent.args ?? []);
|
|
2294
|
-
const valueWei = intent.value ?
|
|
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
|
|
2304
|
-
|
|
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 ?
|
|
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(
|
|
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 =
|
|
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
|
|
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
|
|
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"),
|