logiqical 0.3.1 → 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 +5 -3
package/dist/index.js
CHANGED
|
@@ -33,6 +33,7 @@ __export(index_exports, {
|
|
|
33
33
|
AgentWallet: () => AgentWallet,
|
|
34
34
|
BridgeModule: () => BridgeModule,
|
|
35
35
|
CHAINS: () => CHAINS,
|
|
36
|
+
CopyTradingModule: () => CopyTradingModule,
|
|
36
37
|
DefiModule: () => DefiModule,
|
|
37
38
|
DexModule: () => DexModule,
|
|
38
39
|
LaunchpadModule: () => LaunchpadModule,
|
|
@@ -52,7 +53,7 @@ __export(index_exports, {
|
|
|
52
53
|
module.exports = __toCommonJS(index_exports);
|
|
53
54
|
|
|
54
55
|
// src/client.ts
|
|
55
|
-
var
|
|
56
|
+
var import_ethers10 = require("ethers");
|
|
56
57
|
|
|
57
58
|
// src/wallet.ts
|
|
58
59
|
var import_ethers = require("ethers");
|
|
@@ -490,6 +491,10 @@ var FRACTION_SCALER = 100;
|
|
|
490
491
|
var ARENA_SOCIAL_API = "https://api.starsarena.com";
|
|
491
492
|
var LIFI_API = "https://li.quest/v1";
|
|
492
493
|
var HL_INFO = "https://api.hyperliquid.xyz/info";
|
|
494
|
+
var HL_DEPOSIT_ADDRESS = "0x2Df1c51E09aECF9cacB7bc98cB1742757f163dF7";
|
|
495
|
+
var USDC_ARBITRUM = "0xaf88d065e77c8cC2239327C5EDb3A432268e5831";
|
|
496
|
+
var ARBITRUM_CHAIN_ID = 42161;
|
|
497
|
+
var ARBITRUM_RPC = "https://arb1.arbitrum.io/rpc";
|
|
493
498
|
var DEFAULT_SLIPPAGE_BPS = 500;
|
|
494
499
|
var ERC20_ABI = [
|
|
495
500
|
"function balanceOf(address owner) view returns (uint256)",
|
|
@@ -1186,6 +1191,7 @@ var DexModule = class {
|
|
|
1186
1191
|
};
|
|
1187
1192
|
|
|
1188
1193
|
// src/modules/perps.ts
|
|
1194
|
+
var import_ethers7 = require("ethers");
|
|
1189
1195
|
var PerpsModule = class {
|
|
1190
1196
|
constructor(arenaApiKey) {
|
|
1191
1197
|
this.arenaApiKey = arenaApiKey;
|
|
@@ -1279,6 +1285,51 @@ var PerpsModule = class {
|
|
|
1279
1285
|
async getOpenOrders(wallet) {
|
|
1280
1286
|
return this.hlPost({ type: "openOrders", user: wallet });
|
|
1281
1287
|
}
|
|
1288
|
+
// ── USDC Deposit to Hyperliquid (on Arbitrum) ──
|
|
1289
|
+
/** Check USDC balance on Arbitrum */
|
|
1290
|
+
async getArbitrumUSDCBalance(wallet) {
|
|
1291
|
+
const provider = new import_ethers7.JsonRpcProvider(ARBITRUM_RPC, ARBITRUM_CHAIN_ID);
|
|
1292
|
+
const usdc = new import_ethers7.Contract(USDC_ARBITRUM, ERC20_ABI, provider);
|
|
1293
|
+
const balance = await usdc.balanceOf(wallet);
|
|
1294
|
+
return import_ethers7.ethers.formatUnits(balance, 6);
|
|
1295
|
+
}
|
|
1296
|
+
/** Check ETH balance on Arbitrum (needed for gas) */
|
|
1297
|
+
async getArbitrumETHBalance(wallet) {
|
|
1298
|
+
const provider = new import_ethers7.JsonRpcProvider(ARBITRUM_RPC, ARBITRUM_CHAIN_ID);
|
|
1299
|
+
const balance = await provider.getBalance(wallet);
|
|
1300
|
+
return import_ethers7.ethers.formatEther(balance);
|
|
1301
|
+
}
|
|
1302
|
+
/**
|
|
1303
|
+
* Build a USDC deposit transaction for Hyperliquid on Arbitrum.
|
|
1304
|
+
* Returns an unsigned tx — use with agent.execute() after switching to Arbitrum network.
|
|
1305
|
+
*
|
|
1306
|
+
* Flow: agent.switchNetwork("arbitrum") → agent.execute(agent.perps.buildDepositUSDC(...))
|
|
1307
|
+
*/
|
|
1308
|
+
buildDepositUSDC(amount) {
|
|
1309
|
+
const iface = new import_ethers7.ethers.Interface(["function transfer(address to, uint256 amount) returns (bool)"]);
|
|
1310
|
+
const amountWei = import_ethers7.ethers.parseUnits(amount, 6);
|
|
1311
|
+
const data = iface.encodeFunctionData("transfer", [HL_DEPOSIT_ADDRESS, amountWei]);
|
|
1312
|
+
return {
|
|
1313
|
+
transaction: {
|
|
1314
|
+
to: USDC_ARBITRUM,
|
|
1315
|
+
data,
|
|
1316
|
+
value: "0",
|
|
1317
|
+
chainId: ARBITRUM_CHAIN_ID,
|
|
1318
|
+
description: `Deposit ${amount} USDC to Hyperliquid`
|
|
1319
|
+
}
|
|
1320
|
+
};
|
|
1321
|
+
}
|
|
1322
|
+
/** Get deposit info (addresses + chain details) */
|
|
1323
|
+
getDepositInfo() {
|
|
1324
|
+
return {
|
|
1325
|
+
chain: "Arbitrum One",
|
|
1326
|
+
chainId: ARBITRUM_CHAIN_ID,
|
|
1327
|
+
usdcAddress: USDC_ARBITRUM,
|
|
1328
|
+
depositAddress: HL_DEPOSIT_ADDRESS,
|
|
1329
|
+
decimals: 6,
|
|
1330
|
+
tip: "Bridge USDC to Arbitrum first (use bridge module), then deposit to Hyperliquid"
|
|
1331
|
+
};
|
|
1332
|
+
}
|
|
1282
1333
|
};
|
|
1283
1334
|
|
|
1284
1335
|
// src/modules/bridge.ts
|
|
@@ -1435,37 +1486,37 @@ var BridgeModule = class {
|
|
|
1435
1486
|
};
|
|
1436
1487
|
|
|
1437
1488
|
// src/modules/tickets.ts
|
|
1438
|
-
var
|
|
1489
|
+
var import_ethers8 = require("ethers");
|
|
1439
1490
|
var TicketsModule = class {
|
|
1440
1491
|
constructor(provider) {
|
|
1441
1492
|
this.provider = provider;
|
|
1442
|
-
this.contract = new
|
|
1493
|
+
this.contract = new import_ethers8.ethers.Contract(import_ethers8.ethers.getAddress(ARENA_SHARES_CONTRACT), SHARES_ABI, provider);
|
|
1443
1494
|
}
|
|
1444
1495
|
contract;
|
|
1445
1496
|
async getBuyPrice(subject, amount = "1") {
|
|
1446
1497
|
const fractionalAmount = this.ticketsToFractional(amount);
|
|
1447
|
-
const subjectAddr =
|
|
1498
|
+
const subjectAddr = import_ethers8.ethers.getAddress(subject);
|
|
1448
1499
|
const [price, priceWithFee] = await Promise.all([
|
|
1449
1500
|
this.contract.getBuyPriceForFractionalShares(subjectAddr, fractionalAmount),
|
|
1450
1501
|
this.contract.getBuyPriceForFractionalSharesAfterFee(subjectAddr, fractionalAmount)
|
|
1451
1502
|
]);
|
|
1452
|
-
return { price:
|
|
1503
|
+
return { price: import_ethers8.ethers.formatEther(price), priceWithFee: import_ethers8.ethers.formatEther(priceWithFee), tickets: amount, fractionalAmount };
|
|
1453
1504
|
}
|
|
1454
1505
|
async getSellPrice(subject, amount = "1") {
|
|
1455
1506
|
const fractionalAmount = this.ticketsToFractional(amount);
|
|
1456
|
-
const subjectAddr =
|
|
1507
|
+
const subjectAddr = import_ethers8.ethers.getAddress(subject);
|
|
1457
1508
|
const [price, priceAfterFee] = await Promise.all([
|
|
1458
1509
|
this.contract.getSellPriceForFractionalShares(subjectAddr, fractionalAmount),
|
|
1459
1510
|
this.contract.getSellPriceForFractionalSharesAfterFee(subjectAddr, fractionalAmount)
|
|
1460
1511
|
]);
|
|
1461
|
-
return { price:
|
|
1512
|
+
return { price: import_ethers8.ethers.formatEther(price), priceAfterFee: import_ethers8.ethers.formatEther(priceAfterFee), tickets: amount, fractionalAmount };
|
|
1462
1513
|
}
|
|
1463
1514
|
async getBalance(subject, user) {
|
|
1464
|
-
const frac = await this.contract.getMyFractionalShares(
|
|
1515
|
+
const frac = await this.contract.getMyFractionalShares(import_ethers8.ethers.getAddress(subject), import_ethers8.ethers.getAddress(user));
|
|
1465
1516
|
return { tickets: (Number(frac) / FRACTION_SCALER).toString(), fractionalAmount: frac.toString() };
|
|
1466
1517
|
}
|
|
1467
1518
|
async getSupply(subject) {
|
|
1468
|
-
const subjectAddr =
|
|
1519
|
+
const subjectAddr = import_ethers8.ethers.getAddress(subject);
|
|
1469
1520
|
const [wholeSupply, fracSupply] = await Promise.all([
|
|
1470
1521
|
this.contract.getSharesSupply(subjectAddr),
|
|
1471
1522
|
this.contract.getTotalFractionalSupply(subjectAddr)
|
|
@@ -1483,42 +1534,42 @@ var TicketsModule = class {
|
|
|
1483
1534
|
}
|
|
1484
1535
|
async buildBuyTx(wallet, subject, amount = "1") {
|
|
1485
1536
|
const fractionalAmount = this.ticketsToFractional(amount);
|
|
1486
|
-
const subjectAddr =
|
|
1487
|
-
const userAddr =
|
|
1537
|
+
const subjectAddr = import_ethers8.ethers.getAddress(subject);
|
|
1538
|
+
const userAddr = import_ethers8.ethers.getAddress(wallet);
|
|
1488
1539
|
const cost = await this.contract.getBuyPriceForFractionalSharesAfterFee(subjectAddr, fractionalAmount);
|
|
1489
1540
|
if (cost === 0n) throw new Error("Could not get buy price \u2014 subject may not exist");
|
|
1490
|
-
const iface = new
|
|
1541
|
+
const iface = new import_ethers8.ethers.Interface(SHARES_ABI);
|
|
1491
1542
|
const data = iface.encodeFunctionData("buyFractionalShares", [subjectAddr, userAddr, fractionalAmount]);
|
|
1492
1543
|
return {
|
|
1493
1544
|
transaction: {
|
|
1494
|
-
to:
|
|
1545
|
+
to: import_ethers8.ethers.getAddress(ARENA_SHARES_CONTRACT),
|
|
1495
1546
|
data,
|
|
1496
|
-
value:
|
|
1547
|
+
value: import_ethers8.ethers.toBeHex(cost, 32),
|
|
1497
1548
|
chainId: CHAIN_ID,
|
|
1498
1549
|
gasLimit: "200000",
|
|
1499
|
-
description: `Buy ${amount} ticket(s) for ~${
|
|
1550
|
+
description: `Buy ${amount} ticket(s) for ~${import_ethers8.ethers.formatEther(cost)} AVAX`
|
|
1500
1551
|
}
|
|
1501
1552
|
};
|
|
1502
1553
|
}
|
|
1503
1554
|
async buildSellTx(wallet, subject, amount = "1") {
|
|
1504
1555
|
const fractionalAmount = this.ticketsToFractional(amount);
|
|
1505
|
-
const subjectAddr =
|
|
1506
|
-
const userAddr =
|
|
1556
|
+
const subjectAddr = import_ethers8.ethers.getAddress(subject);
|
|
1557
|
+
const userAddr = import_ethers8.ethers.getAddress(wallet);
|
|
1507
1558
|
const balance = await this.contract.getMyFractionalShares(subjectAddr, userAddr);
|
|
1508
1559
|
if (balance < BigInt(fractionalAmount)) {
|
|
1509
1560
|
throw new Error(`Insufficient tickets: have ${Number(balance) / FRACTION_SCALER}, trying to sell ${amount}`);
|
|
1510
1561
|
}
|
|
1511
1562
|
const proceeds = await this.contract.getSellPriceForFractionalSharesAfterFee(subjectAddr, fractionalAmount);
|
|
1512
|
-
const iface = new
|
|
1563
|
+
const iface = new import_ethers8.ethers.Interface(SHARES_ABI);
|
|
1513
1564
|
const data = iface.encodeFunctionData("sellFractionalShares", [subjectAddr, userAddr, fractionalAmount]);
|
|
1514
1565
|
return {
|
|
1515
1566
|
transaction: {
|
|
1516
|
-
to:
|
|
1567
|
+
to: import_ethers8.ethers.getAddress(ARENA_SHARES_CONTRACT),
|
|
1517
1568
|
data,
|
|
1518
1569
|
value: "0x0",
|
|
1519
1570
|
chainId: CHAIN_ID,
|
|
1520
1571
|
gasLimit: "200000",
|
|
1521
|
-
description: `Sell ${amount} ticket(s) for ~${
|
|
1572
|
+
description: `Sell ${amount} ticket(s) for ~${import_ethers8.ethers.formatEther(proceeds)} AVAX`
|
|
1522
1573
|
}
|
|
1523
1574
|
};
|
|
1524
1575
|
}
|
|
@@ -1537,6 +1588,53 @@ var SocialModule = class {
|
|
|
1537
1588
|
setArenaApiKey(key) {
|
|
1538
1589
|
this.arenaApiKey = key;
|
|
1539
1590
|
}
|
|
1591
|
+
// ── Agent Registration (no auth needed — returns API key) ──
|
|
1592
|
+
/**
|
|
1593
|
+
* Register a new AI agent on Arena. Returns API key (shown once — save immediately).
|
|
1594
|
+
* After registration, owner must claim by posting: "I'm claiming my AI Agent \"<name>\"\nVerification Code: <code>"
|
|
1595
|
+
*/
|
|
1596
|
+
static async registerAgent(opts) {
|
|
1597
|
+
const res = await fetch(`${ARENA_SOCIAL_API}/agents/register`, {
|
|
1598
|
+
method: "POST",
|
|
1599
|
+
headers: { "Content-Type": "application/json" },
|
|
1600
|
+
body: JSON.stringify(opts)
|
|
1601
|
+
});
|
|
1602
|
+
const data = await res.json();
|
|
1603
|
+
if (!res.ok) throw new Error(data.message || data.error || `Registration failed (${res.status})`);
|
|
1604
|
+
return data;
|
|
1605
|
+
}
|
|
1606
|
+
// ── Feed Auto-Posting (trade updates) ──
|
|
1607
|
+
/** Post a formatted trade update to the Arena feed */
|
|
1608
|
+
async postTradeUpdate(trade) {
|
|
1609
|
+
const lines = [];
|
|
1610
|
+
const emoji = {
|
|
1611
|
+
buy: "\u{1F7E2}",
|
|
1612
|
+
sell: "\u{1F534}",
|
|
1613
|
+
swap: "\u{1F504}",
|
|
1614
|
+
bridge: "\u{1F309}",
|
|
1615
|
+
stake: "\u{1F512}",
|
|
1616
|
+
long: "\u{1F4C8}",
|
|
1617
|
+
short: "\u{1F4C9}",
|
|
1618
|
+
close: "\u2705"
|
|
1619
|
+
};
|
|
1620
|
+
const icon = emoji[trade.action] || "\u{1F4B0}";
|
|
1621
|
+
if (trade.action === "swap" && trade.fromToken && trade.toToken) {
|
|
1622
|
+
lines.push(`${icon} Swapped ${trade.amount || ""} ${trade.fromToken} \u2192 ${trade.toToken}`);
|
|
1623
|
+
} else if (trade.action === "bridge" && trade.fromToken) {
|
|
1624
|
+
lines.push(`${icon} Bridged ${trade.amount || ""} ${trade.fromToken}`);
|
|
1625
|
+
} else if (trade.action === "long" || trade.action === "short") {
|
|
1626
|
+
lines.push(`${icon} Opened ${trade.action.toUpperCase()} ${trade.token || ""} ${trade.amount ? `(${trade.amount})` : ""}`);
|
|
1627
|
+
} else if (trade.action === "close") {
|
|
1628
|
+
lines.push(`${icon} Closed ${trade.token || ""} position${trade.pnl ? ` | PnL: ${trade.pnl}` : ""}`);
|
|
1629
|
+
} else {
|
|
1630
|
+
lines.push(`${icon} ${trade.action.toUpperCase()} ${trade.amount || ""} ${trade.token || ""}`);
|
|
1631
|
+
}
|
|
1632
|
+
if (trade.price) lines.push(`Price: $${trade.price}`);
|
|
1633
|
+
if (trade.hash) lines.push(`tx: ${trade.hash}`);
|
|
1634
|
+
if (trade.extra) lines.push(trade.extra);
|
|
1635
|
+
const content = lines.join("<br>");
|
|
1636
|
+
return this.createThread(content);
|
|
1637
|
+
}
|
|
1540
1638
|
async request(method, path, body, query) {
|
|
1541
1639
|
if (!this.arenaApiKey) throw new Error("Arena API key required for social endpoints. Pass arenaApiKey in config.");
|
|
1542
1640
|
let url = `${ARENA_SOCIAL_API}${path}`;
|
|
@@ -1990,7 +2088,7 @@ var MarketModule = class {
|
|
|
1990
2088
|
};
|
|
1991
2089
|
|
|
1992
2090
|
// src/modules/defi.ts
|
|
1993
|
-
var
|
|
2091
|
+
var import_ethers9 = require("ethers");
|
|
1994
2092
|
var SAVAX_ADDRESS = "0x2b2C81e08f1Af8835a78Bb2A90AE924ACE0eA4bE";
|
|
1995
2093
|
var SAVAX_ABI = [
|
|
1996
2094
|
"function submit() payable returns (uint256)",
|
|
@@ -2031,7 +2129,7 @@ var ERC20_ABI2 = [
|
|
|
2031
2129
|
var DefiModule = class {
|
|
2032
2130
|
constructor(provider) {
|
|
2033
2131
|
this.provider = provider;
|
|
2034
|
-
this.savax = new
|
|
2132
|
+
this.savax = new import_ethers9.ethers.Contract(import_ethers9.ethers.getAddress(SAVAX_ADDRESS), SAVAX_ABI, provider);
|
|
2035
2133
|
}
|
|
2036
2134
|
savax;
|
|
2037
2135
|
// ── sAVAX Liquid Staking ──
|
|
@@ -2041,36 +2139,36 @@ var DefiModule = class {
|
|
|
2041
2139
|
this.savax.totalPooledAvax(),
|
|
2042
2140
|
this.savax.totalShares()
|
|
2043
2141
|
]);
|
|
2044
|
-
const exchangeRate = totalShares > 0n ?
|
|
2142
|
+
const exchangeRate = totalShares > 0n ? import_ethers9.ethers.formatEther(totalPooled * import_ethers9.ethers.parseEther("1") / totalShares) : "1.0";
|
|
2045
2143
|
const result = {
|
|
2046
2144
|
exchangeRate,
|
|
2047
|
-
totalPooledAvax:
|
|
2048
|
-
totalShares:
|
|
2145
|
+
totalPooledAvax: import_ethers9.ethers.formatEther(totalPooled),
|
|
2146
|
+
totalShares: import_ethers9.ethers.formatEther(totalShares)
|
|
2049
2147
|
};
|
|
2050
2148
|
if (wallet) {
|
|
2051
2149
|
const balance = await this.savax.balanceOf(wallet);
|
|
2052
2150
|
const avaxValue = await this.savax.getPooledAvaxByShares(balance);
|
|
2053
|
-
result.balance =
|
|
2054
|
-
result.balanceInAvax =
|
|
2151
|
+
result.balance = import_ethers9.ethers.formatEther(balance);
|
|
2152
|
+
result.balanceInAvax = import_ethers9.ethers.formatEther(avaxValue);
|
|
2055
2153
|
}
|
|
2056
2154
|
return result;
|
|
2057
2155
|
}
|
|
2058
2156
|
/** Quote: how much sAVAX for staking AVAX */
|
|
2059
2157
|
async sAvaxStakeQuote(avaxAmount) {
|
|
2060
|
-
const avaxWei =
|
|
2158
|
+
const avaxWei = import_ethers9.ethers.parseEther(avaxAmount);
|
|
2061
2159
|
const shares = await this.savax.getSharesByPooledAvax(avaxWei);
|
|
2062
|
-
return { avaxIn: avaxAmount, savaxOut:
|
|
2160
|
+
return { avaxIn: avaxAmount, savaxOut: import_ethers9.ethers.formatEther(shares) };
|
|
2063
2161
|
}
|
|
2064
2162
|
/** Build tx to stake AVAX → sAVAX */
|
|
2065
2163
|
async buildSAvaxStake(avaxAmount) {
|
|
2066
|
-
const avaxWei =
|
|
2067
|
-
const iface = new
|
|
2164
|
+
const avaxWei = import_ethers9.ethers.parseEther(avaxAmount);
|
|
2165
|
+
const iface = new import_ethers9.ethers.Interface(SAVAX_ABI);
|
|
2068
2166
|
const data = iface.encodeFunctionData("submit", []);
|
|
2069
2167
|
return {
|
|
2070
2168
|
transactions: [{
|
|
2071
|
-
to:
|
|
2169
|
+
to: import_ethers9.ethers.getAddress(SAVAX_ADDRESS),
|
|
2072
2170
|
data,
|
|
2073
|
-
value:
|
|
2171
|
+
value: import_ethers9.ethers.toBeHex(avaxWei, 32),
|
|
2074
2172
|
chainId: CHAIN_ID,
|
|
2075
2173
|
gasLimit: "300000",
|
|
2076
2174
|
description: `Stake ${avaxAmount} AVAX \u2192 sAVAX`
|
|
@@ -2083,26 +2181,26 @@ var DefiModule = class {
|
|
|
2083
2181
|
if (amount === "max") {
|
|
2084
2182
|
shareAmount = await this.savax.balanceOf(wallet);
|
|
2085
2183
|
} else {
|
|
2086
|
-
shareAmount =
|
|
2184
|
+
shareAmount = import_ethers9.ethers.parseEther(amount);
|
|
2087
2185
|
}
|
|
2088
2186
|
if (shareAmount === 0n) throw new Error("Zero sAVAX balance");
|
|
2089
|
-
const iface = new
|
|
2187
|
+
const iface = new import_ethers9.ethers.Interface(SAVAX_ABI);
|
|
2090
2188
|
const data = iface.encodeFunctionData("requestUnlock", [shareAmount]);
|
|
2091
2189
|
return {
|
|
2092
2190
|
transactions: [{
|
|
2093
|
-
to:
|
|
2191
|
+
to: import_ethers9.ethers.getAddress(SAVAX_ADDRESS),
|
|
2094
2192
|
data,
|
|
2095
2193
|
value: "0",
|
|
2096
2194
|
chainId: CHAIN_ID,
|
|
2097
2195
|
gasLimit: "300000",
|
|
2098
|
-
description: `Request unstake ${
|
|
2196
|
+
description: `Request unstake ${import_ethers9.ethers.formatEther(shareAmount)} sAVAX`
|
|
2099
2197
|
}]
|
|
2100
2198
|
};
|
|
2101
2199
|
}
|
|
2102
2200
|
// ── ERC-4626 Vaults ──
|
|
2103
2201
|
/** Get info about any ERC-4626 vault */
|
|
2104
2202
|
async vaultInfo(vaultAddress, wallet) {
|
|
2105
|
-
const vault = new
|
|
2203
|
+
const vault = new import_ethers9.ethers.Contract(import_ethers9.ethers.getAddress(vaultAddress), VAULT_ABI, this.provider);
|
|
2106
2204
|
const [name, symbol, asset, totalAssets, totalSupply, decimals] = await Promise.all([
|
|
2107
2205
|
vault.name(),
|
|
2108
2206
|
vault.symbol(),
|
|
@@ -2111,70 +2209,70 @@ var DefiModule = class {
|
|
|
2111
2209
|
vault.totalSupply(),
|
|
2112
2210
|
vault.decimals()
|
|
2113
2211
|
]);
|
|
2114
|
-
const sharePrice = totalSupply > 0n ?
|
|
2212
|
+
const sharePrice = totalSupply > 0n ? import_ethers9.ethers.formatUnits(totalAssets * 10n ** BigInt(decimals) / totalSupply, decimals) : "1.0";
|
|
2115
2213
|
const result = {
|
|
2116
2214
|
name,
|
|
2117
2215
|
symbol,
|
|
2118
2216
|
asset,
|
|
2119
|
-
totalAssets:
|
|
2120
|
-
totalSupply:
|
|
2217
|
+
totalAssets: import_ethers9.ethers.formatUnits(totalAssets, decimals),
|
|
2218
|
+
totalSupply: import_ethers9.ethers.formatUnits(totalSupply, decimals),
|
|
2121
2219
|
sharePrice
|
|
2122
2220
|
};
|
|
2123
2221
|
if (wallet) {
|
|
2124
2222
|
const shares = await vault.balanceOf(wallet);
|
|
2125
2223
|
const assets = shares > 0n ? await vault.convertToAssets(shares) : 0n;
|
|
2126
|
-
result.userShares =
|
|
2127
|
-
result.userAssets =
|
|
2224
|
+
result.userShares = import_ethers9.ethers.formatUnits(shares, decimals);
|
|
2225
|
+
result.userAssets = import_ethers9.ethers.formatUnits(assets, decimals);
|
|
2128
2226
|
}
|
|
2129
2227
|
return result;
|
|
2130
2228
|
}
|
|
2131
2229
|
/** Quote vault deposit — how many shares for given assets */
|
|
2132
2230
|
async vaultDepositQuote(vaultAddress, amount) {
|
|
2133
|
-
const vault = new
|
|
2231
|
+
const vault = new import_ethers9.ethers.Contract(import_ethers9.ethers.getAddress(vaultAddress), VAULT_ABI, this.provider);
|
|
2134
2232
|
const decimals = Number(await vault.decimals());
|
|
2135
|
-
const assetsWei =
|
|
2233
|
+
const assetsWei = import_ethers9.ethers.parseUnits(amount, decimals);
|
|
2136
2234
|
const shares = await vault.previewDeposit(assetsWei);
|
|
2137
|
-
return { assetsIn: amount, sharesOut:
|
|
2235
|
+
return { assetsIn: amount, sharesOut: import_ethers9.ethers.formatUnits(shares, decimals) };
|
|
2138
2236
|
}
|
|
2139
2237
|
/** Build txs to deposit into an ERC-4626 vault: [approve, deposit] */
|
|
2140
2238
|
async buildVaultDeposit(wallet, vaultAddress, amount) {
|
|
2141
|
-
const vaultAddr =
|
|
2142
|
-
const vault = new
|
|
2239
|
+
const vaultAddr = import_ethers9.ethers.getAddress(vaultAddress);
|
|
2240
|
+
const vault = new import_ethers9.ethers.Contract(vaultAddr, VAULT_ABI, this.provider);
|
|
2143
2241
|
const assetAddr = await vault.asset();
|
|
2144
|
-
const assetToken = new
|
|
2242
|
+
const assetToken = new import_ethers9.ethers.Contract(assetAddr, ERC20_ABI2, this.provider);
|
|
2145
2243
|
const decimals = Number(await assetToken.decimals());
|
|
2146
2244
|
let depositAmount;
|
|
2147
2245
|
if (amount === "max") {
|
|
2148
2246
|
depositAmount = await assetToken.balanceOf(wallet);
|
|
2149
2247
|
} else {
|
|
2150
|
-
depositAmount =
|
|
2248
|
+
depositAmount = import_ethers9.ethers.parseUnits(amount, decimals);
|
|
2151
2249
|
}
|
|
2152
2250
|
if (depositAmount === 0n) throw new Error("Zero balance to deposit");
|
|
2153
|
-
const erc20Iface = new
|
|
2251
|
+
const erc20Iface = new import_ethers9.ethers.Interface(ERC20_ABI2);
|
|
2154
2252
|
const approveData = erc20Iface.encodeFunctionData("approve", [vaultAddr, depositAmount]);
|
|
2155
|
-
const vaultIface = new
|
|
2253
|
+
const vaultIface = new import_ethers9.ethers.Interface(VAULT_ABI);
|
|
2156
2254
|
const depositData = vaultIface.encodeFunctionData("deposit", [depositAmount, wallet]);
|
|
2157
2255
|
return {
|
|
2158
2256
|
transactions: [
|
|
2159
2257
|
{ to: assetAddr, data: approveData, value: "0", chainId: CHAIN_ID, gasLimit: "60000", description: `Approve asset for vault deposit` },
|
|
2160
|
-
{ to: vaultAddr, data: depositData, value: "0", chainId: CHAIN_ID, gasLimit: "300000", description: `Deposit ${
|
|
2258
|
+
{ to: vaultAddr, data: depositData, value: "0", chainId: CHAIN_ID, gasLimit: "300000", description: `Deposit ${import_ethers9.ethers.formatUnits(depositAmount, decimals)} into vault` }
|
|
2161
2259
|
]
|
|
2162
2260
|
};
|
|
2163
2261
|
}
|
|
2164
2262
|
/** Build tx to withdraw from an ERC-4626 vault */
|
|
2165
2263
|
async buildVaultWithdraw(wallet, vaultAddress, amount) {
|
|
2166
|
-
const vaultAddr =
|
|
2167
|
-
const vault = new
|
|
2264
|
+
const vaultAddr = import_ethers9.ethers.getAddress(vaultAddress);
|
|
2265
|
+
const vault = new import_ethers9.ethers.Contract(vaultAddr, VAULT_ABI, this.provider);
|
|
2168
2266
|
const decimals = Number(await vault.decimals());
|
|
2169
2267
|
let withdrawAmount;
|
|
2170
2268
|
if (amount === "max") {
|
|
2171
2269
|
const shares = await vault.balanceOf(wallet);
|
|
2172
2270
|
withdrawAmount = await vault.convertToAssets(shares);
|
|
2173
2271
|
} else {
|
|
2174
|
-
withdrawAmount =
|
|
2272
|
+
withdrawAmount = import_ethers9.ethers.parseUnits(amount, decimals);
|
|
2175
2273
|
}
|
|
2176
2274
|
if (withdrawAmount === 0n) throw new Error("Nothing to withdraw");
|
|
2177
|
-
const iface = new
|
|
2275
|
+
const iface = new import_ethers9.ethers.Interface(VAULT_ABI);
|
|
2178
2276
|
const data = iface.encodeFunctionData("withdraw", [withdrawAmount, wallet, wallet]);
|
|
2179
2277
|
return {
|
|
2180
2278
|
transactions: [{
|
|
@@ -2183,12 +2281,139 @@ var DefiModule = class {
|
|
|
2183
2281
|
value: "0",
|
|
2184
2282
|
chainId: CHAIN_ID,
|
|
2185
2283
|
gasLimit: "300000",
|
|
2186
|
-
description: `Withdraw ${
|
|
2284
|
+
description: `Withdraw ${import_ethers9.ethers.formatUnits(withdrawAmount, decimals)} from vault`
|
|
2187
2285
|
}]
|
|
2188
2286
|
};
|
|
2189
2287
|
}
|
|
2190
2288
|
};
|
|
2191
2289
|
|
|
2290
|
+
// src/modules/copytrading.ts
|
|
2291
|
+
var CopyTradingModule = class {
|
|
2292
|
+
perps;
|
|
2293
|
+
constructor(perps) {
|
|
2294
|
+
this.perps = perps;
|
|
2295
|
+
}
|
|
2296
|
+
/** Fetch all open positions for a Hyperliquid wallet */
|
|
2297
|
+
async getTargetPositions(targetWallet) {
|
|
2298
|
+
const state = await this.perps.getPositions(targetWallet);
|
|
2299
|
+
const assetPositions = state?.assetPositions ?? [];
|
|
2300
|
+
return assetPositions.map((ap) => ap.position).filter((p) => p && parseFloat(p.szi) !== 0);
|
|
2301
|
+
}
|
|
2302
|
+
/** Fetch your agent's current positions */
|
|
2303
|
+
async getAgentPositions(agentWallet) {
|
|
2304
|
+
const state = await this.perps.getPositions(agentWallet);
|
|
2305
|
+
const assetPositions = state?.assetPositions ?? [];
|
|
2306
|
+
return assetPositions.map((ap) => ap.position).filter((p) => p && parseFloat(p.szi) !== 0);
|
|
2307
|
+
}
|
|
2308
|
+
/**
|
|
2309
|
+
* Compare target wallet positions with agent positions and return orders needed to mirror.
|
|
2310
|
+
* Uses proportional sizing based on the scale factor.
|
|
2311
|
+
*
|
|
2312
|
+
* @param targetWallet - Wallet to copy from
|
|
2313
|
+
* @param agentWallet - Your agent's wallet
|
|
2314
|
+
* @param scaleFactor - Position scale (0.1 = 10% of target size, 1.0 = same size)
|
|
2315
|
+
*/
|
|
2316
|
+
async calculateMirrorOrders(targetWallet, agentWallet, scaleFactor = 0.1) {
|
|
2317
|
+
const [targetPositions, agentPositions] = await Promise.all([
|
|
2318
|
+
this.getTargetPositions(targetWallet),
|
|
2319
|
+
this.getAgentPositions(agentWallet)
|
|
2320
|
+
]);
|
|
2321
|
+
const agentMap = /* @__PURE__ */ new Map();
|
|
2322
|
+
for (const p of agentPositions) {
|
|
2323
|
+
agentMap.set(p.coin, p);
|
|
2324
|
+
}
|
|
2325
|
+
const targetMap = /* @__PURE__ */ new Map();
|
|
2326
|
+
for (const p of targetPositions) {
|
|
2327
|
+
targetMap.set(p.coin, p);
|
|
2328
|
+
}
|
|
2329
|
+
const orders = [];
|
|
2330
|
+
for (const [coin, targetPos] of targetMap) {
|
|
2331
|
+
const targetSize = parseFloat(targetPos.szi);
|
|
2332
|
+
const direction = targetSize > 0 ? "long" : "short";
|
|
2333
|
+
const desiredSize = Math.abs(targetSize) * scaleFactor;
|
|
2334
|
+
const agentPos = agentMap.get(coin);
|
|
2335
|
+
if (!agentPos) {
|
|
2336
|
+
orders.push({ symbol: coin, direction, size: desiredSize, action: "open", reason: `Mirror ${coin} ${direction} from target` });
|
|
2337
|
+
} else {
|
|
2338
|
+
const agentSize = parseFloat(agentPos.szi);
|
|
2339
|
+
const agentDir = agentSize > 0 ? "long" : "short";
|
|
2340
|
+
if (agentDir !== direction) {
|
|
2341
|
+
orders.push({ symbol: coin, direction: agentDir, size: Math.abs(agentSize), action: "close", reason: `Close ${agentDir} to flip to ${direction}` });
|
|
2342
|
+
orders.push({ symbol: coin, direction, size: desiredSize, action: "open", reason: `Mirror ${coin} ${direction} from target` });
|
|
2343
|
+
}
|
|
2344
|
+
}
|
|
2345
|
+
}
|
|
2346
|
+
for (const [coin, agentPos] of agentMap) {
|
|
2347
|
+
if (!targetMap.has(coin)) {
|
|
2348
|
+
const agentSize = parseFloat(agentPos.szi);
|
|
2349
|
+
const dir = agentSize > 0 ? "long" : "short";
|
|
2350
|
+
orders.push({ symbol: coin, direction: dir, size: Math.abs(agentSize), action: "close", reason: `Target closed ${coin} position` });
|
|
2351
|
+
}
|
|
2352
|
+
}
|
|
2353
|
+
return { orders, targetPositions, agentPositions };
|
|
2354
|
+
}
|
|
2355
|
+
/**
|
|
2356
|
+
* Execute mirror orders via the perps module (Arena API).
|
|
2357
|
+
* Returns results for each order.
|
|
2358
|
+
*/
|
|
2359
|
+
async executeMirrorOrders(orders, currentPrices) {
|
|
2360
|
+
const results = [];
|
|
2361
|
+
for (const order of orders) {
|
|
2362
|
+
const price = currentPrices[order.symbol];
|
|
2363
|
+
if (!price) {
|
|
2364
|
+
results.push({ order, error: `No price for ${order.symbol}` });
|
|
2365
|
+
continue;
|
|
2366
|
+
}
|
|
2367
|
+
try {
|
|
2368
|
+
if (order.action === "close") {
|
|
2369
|
+
const result = await this.perps.closePosition(order.symbol, order.direction, order.size, price);
|
|
2370
|
+
results.push({ order, result });
|
|
2371
|
+
} else {
|
|
2372
|
+
const result = await this.perps.placeOrder([{
|
|
2373
|
+
symbol: order.symbol,
|
|
2374
|
+
direction: order.direction,
|
|
2375
|
+
orderType: "market",
|
|
2376
|
+
leverageType: "cross",
|
|
2377
|
+
size: order.size,
|
|
2378
|
+
leverage: 1
|
|
2379
|
+
}]);
|
|
2380
|
+
results.push({ order, result });
|
|
2381
|
+
}
|
|
2382
|
+
} catch (e) {
|
|
2383
|
+
results.push({ order, error: e.message });
|
|
2384
|
+
}
|
|
2385
|
+
}
|
|
2386
|
+
return results;
|
|
2387
|
+
}
|
|
2388
|
+
/**
|
|
2389
|
+
* One-shot copy: calculate + execute mirror orders in one call.
|
|
2390
|
+
* Fetches current prices from Hyperliquid for execution.
|
|
2391
|
+
*/
|
|
2392
|
+
async copyOnce(targetWallet, agentWallet, scaleFactor = 0.1) {
|
|
2393
|
+
const { orders } = await this.calculateMirrorOrders(targetWallet, agentWallet, scaleFactor);
|
|
2394
|
+
if (orders.length === 0) return { orders, results: [] };
|
|
2395
|
+
const pairs = await this.perps.getTradingPairs();
|
|
2396
|
+
const midPrices = await this.fetchMidPrices(orders.map((o) => o.symbol), pairs.pairs);
|
|
2397
|
+
const results = await this.executeMirrorOrders(orders, midPrices);
|
|
2398
|
+
return { orders, results };
|
|
2399
|
+
}
|
|
2400
|
+
/** Fetch mid-market prices from Hyperliquid for given symbols */
|
|
2401
|
+
async fetchMidPrices(symbols, pairs) {
|
|
2402
|
+
const res = await fetch(HL_INFO, {
|
|
2403
|
+
method: "POST",
|
|
2404
|
+
headers: { "Content-Type": "application/json" },
|
|
2405
|
+
body: JSON.stringify({ type: "allMids" })
|
|
2406
|
+
});
|
|
2407
|
+
if (!res.ok) throw new Error(`Failed to fetch mid prices: ${res.status}`);
|
|
2408
|
+
const mids = await res.json();
|
|
2409
|
+
const prices = {};
|
|
2410
|
+
for (const sym of symbols) {
|
|
2411
|
+
if (mids[sym]) prices[sym] = parseFloat(mids[sym]);
|
|
2412
|
+
}
|
|
2413
|
+
return prices;
|
|
2414
|
+
}
|
|
2415
|
+
};
|
|
2416
|
+
|
|
2192
2417
|
// src/client.ts
|
|
2193
2418
|
var Logiqical = class _Logiqical {
|
|
2194
2419
|
/** Buy/sell ARENA tokens via LFJ DEX */
|
|
@@ -2213,6 +2438,8 @@ var Logiqical = class _Logiqical {
|
|
|
2213
2438
|
market;
|
|
2214
2439
|
/** DeFi — sAVAX liquid staking + ERC-4626 vaults */
|
|
2215
2440
|
defi;
|
|
2441
|
+
/** Copy trading — mirror Hyperliquid wallet positions */
|
|
2442
|
+
copyTrading;
|
|
2216
2443
|
/** The agent's wallet address */
|
|
2217
2444
|
address;
|
|
2218
2445
|
/** The local wallet (null if read-only mode) */
|
|
@@ -2233,7 +2460,7 @@ var Logiqical = class _Logiqical {
|
|
|
2233
2460
|
this.provider = this.agentWallet.provider;
|
|
2234
2461
|
this.address = this.agentWallet.address;
|
|
2235
2462
|
} else if (config.mnemonic) {
|
|
2236
|
-
const hdWallet =
|
|
2463
|
+
const hdWallet = import_ethers10.ethers.HDNodeWallet.fromPhrase(config.mnemonic);
|
|
2237
2464
|
this.agentWallet = AgentWallet.fromPrivateKey(hdWallet.privateKey, {
|
|
2238
2465
|
network: config.network,
|
|
2239
2466
|
rpcUrl: config.rpcUrl
|
|
@@ -2259,6 +2486,7 @@ var Logiqical = class _Logiqical {
|
|
|
2259
2486
|
this.signals = new SignalsModule();
|
|
2260
2487
|
this.market = new MarketModule();
|
|
2261
2488
|
this.defi = new DefiModule(this.provider);
|
|
2489
|
+
this.copyTrading = new CopyTradingModule(this.perps);
|
|
2262
2490
|
}
|
|
2263
2491
|
// ── Factory Methods ──
|
|
2264
2492
|
/** Generate a brand new agent wallet */
|
|
@@ -2334,9 +2562,9 @@ var Logiqical = class _Logiqical {
|
|
|
2334
2562
|
*/
|
|
2335
2563
|
async call(intent) {
|
|
2336
2564
|
this.requireWallet("call");
|
|
2337
|
-
const iface = new
|
|
2565
|
+
const iface = new import_ethers10.ethers.Interface(intent.abi);
|
|
2338
2566
|
const data = iface.encodeFunctionData(intent.method, intent.args ?? []);
|
|
2339
|
-
const valueWei = intent.value ?
|
|
2567
|
+
const valueWei = intent.value ? import_ethers10.ethers.parseEther(intent.value).toString() : "0";
|
|
2340
2568
|
const policyTx = { to: intent.contract, data, value: valueWei, chainId: 43114 };
|
|
2341
2569
|
this.policyEngine.check(policyTx);
|
|
2342
2570
|
if (this.policyEngine.shouldSimulate) {
|
|
@@ -2345,22 +2573,22 @@ var Logiqical = class _Logiqical {
|
|
|
2345
2573
|
if (this.policyEngine.isDryRun) {
|
|
2346
2574
|
return { hash: "0x_dry_run", receipt: { status: 1, blockNumber: 0, gasUsed: "0", transactionHash: "0x_dry_run" } };
|
|
2347
2575
|
}
|
|
2348
|
-
const contract = new
|
|
2349
|
-
|
|
2576
|
+
const contract = new import_ethers10.Contract(
|
|
2577
|
+
import_ethers10.ethers.getAddress(intent.contract),
|
|
2350
2578
|
intent.abi,
|
|
2351
2579
|
this.agentWallet.wallet
|
|
2352
2580
|
);
|
|
2353
2581
|
const tx = await contract[intent.method](
|
|
2354
2582
|
...intent.args ?? [],
|
|
2355
2583
|
{
|
|
2356
|
-
value: intent.value ?
|
|
2584
|
+
value: intent.value ? import_ethers10.ethers.parseEther(intent.value) : void 0,
|
|
2357
2585
|
gasLimit: intent.gasLimit ?? void 0
|
|
2358
2586
|
}
|
|
2359
2587
|
);
|
|
2360
2588
|
const receipt = await tx.wait(1);
|
|
2361
2589
|
if (!receipt) throw new Error(`Transaction ${tx.hash} failed \u2014 no receipt.`);
|
|
2362
2590
|
if (intent.value) {
|
|
2363
|
-
this.policyEngine.recordSpend(
|
|
2591
|
+
this.policyEngine.recordSpend(import_ethers10.ethers.parseEther(intent.value));
|
|
2364
2592
|
}
|
|
2365
2593
|
return {
|
|
2366
2594
|
hash: tx.hash,
|
|
@@ -2375,7 +2603,7 @@ var Logiqical = class _Logiqical {
|
|
|
2375
2603
|
/** Send native token (AVAX) — policy check + simulate + sign + broadcast */
|
|
2376
2604
|
async send(to, amount) {
|
|
2377
2605
|
this.requireWallet("send");
|
|
2378
|
-
const valueWei =
|
|
2606
|
+
const valueWei = import_ethers10.ethers.parseEther(amount);
|
|
2379
2607
|
const policyTx = { to, data: "0x", value: valueWei.toString(), chainId: 43114 };
|
|
2380
2608
|
this.policyEngine.check(policyTx);
|
|
2381
2609
|
if (this.policyEngine.isDryRun) {
|
|
@@ -2442,7 +2670,7 @@ var Logiqical = class _Logiqical {
|
|
|
2442
2670
|
async getBalance() {
|
|
2443
2671
|
if (this.agentWallet) return this.agentWallet.getBalance();
|
|
2444
2672
|
const balance = await this.provider.getBalance(this.address);
|
|
2445
|
-
return
|
|
2673
|
+
return import_ethers10.ethers.formatEther(balance);
|
|
2446
2674
|
}
|
|
2447
2675
|
/** Sign a message */
|
|
2448
2676
|
async signMessage(message) {
|
|
@@ -2493,7 +2721,7 @@ var Logiqical = class _Logiqical {
|
|
|
2493
2721
|
}
|
|
2494
2722
|
const rpcUrl = config.rpcUrl ?? chain.rpcUrl;
|
|
2495
2723
|
const chainId = chain?.chainId ?? void 0;
|
|
2496
|
-
return { provider: new
|
|
2724
|
+
return { provider: new import_ethers10.JsonRpcProvider(rpcUrl, chainId) };
|
|
2497
2725
|
}
|
|
2498
2726
|
};
|
|
2499
2727
|
var LogiqicalClient = Logiqical;
|
|
@@ -2517,6 +2745,7 @@ var LogiqicalError = class _LogiqicalError extends Error {
|
|
|
2517
2745
|
AgentWallet,
|
|
2518
2746
|
BridgeModule,
|
|
2519
2747
|
CHAINS,
|
|
2748
|
+
CopyTradingModule,
|
|
2520
2749
|
DefiModule,
|
|
2521
2750
|
DexModule,
|
|
2522
2751
|
LaunchpadModule,
|