@exagent/agent 0.1.45 → 0.1.47

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -610,6 +610,30 @@ var MarketDataService = class {
610
610
  }
611
611
  };
612
612
  }
613
+ /**
614
+ * Discover all ERC-20 token holdings for a wallet via Blockscout API.
615
+ * Returns token addresses with non-zero balances.
616
+ * Used by Frontier agents to find tokens outside the standard whitelist.
617
+ */
618
+ async discoverTokenHoldings(walletAddress) {
619
+ try {
620
+ const resp = await fetch(
621
+ `https://base.blockscout.com/api/v2/addresses/${walletAddress}/token-balances`,
622
+ { signal: AbortSignal.timeout(1e4) }
623
+ );
624
+ if (!resp.ok) return [];
625
+ const data = await resp.json();
626
+ const addresses = [];
627
+ for (const entry of data) {
628
+ if (entry.token?.type === "ERC-20" && entry.value && entry.value !== "0") {
629
+ addresses.push(entry.token.address.toLowerCase());
630
+ }
631
+ }
632
+ return addresses;
633
+ } catch {
634
+ return [];
635
+ }
636
+ }
613
637
  /**
614
638
  * Check if cached prices are still fresh
615
639
  */
@@ -2353,16 +2377,25 @@ var TradeExecutor = class {
2353
2377
  config;
2354
2378
  allowedTokens;
2355
2379
  configHashFn;
2356
- constructor(client, config, configHashFn) {
2380
+ vaultManager;
2381
+ constructor(client, config, configHashFn, vaultManager) {
2357
2382
  this.client = client;
2358
2383
  this.config = config;
2359
2384
  this.configHashFn = configHashFn;
2385
+ this.vaultManager = vaultManager;
2360
2386
  this.allowedTokens = new Set(
2361
2387
  (config.allowedTokens || []).map((t) => t.toLowerCase())
2362
2388
  );
2363
2389
  }
2364
2390
  /**
2365
- * Execute a single trade signal
2391
+ * Set the vault manager (allows wiring after construction)
2392
+ */
2393
+ setVaultManager(vm) {
2394
+ this.vaultManager = vm;
2395
+ }
2396
+ /**
2397
+ * Execute a single trade signal.
2398
+ * Tries vault trading first if vault exists, falls back to wallet trading.
2366
2399
  */
2367
2400
  async execute(signal) {
2368
2401
  if (signal.action === "hold") {
@@ -2375,13 +2408,38 @@ var TradeExecutor = class {
2375
2408
  return { success: false, error: "Signal exceeds position limits" };
2376
2409
  }
2377
2410
  const configHash = this.configHashFn?.();
2378
- const result = await this.client.trade({
2411
+ const tradeIntent = {
2379
2412
  tokenIn: signal.tokenIn,
2380
2413
  tokenOut: signal.tokenOut,
2381
2414
  amountIn: signal.amountIn,
2382
2415
  maxSlippageBps: this.config.trading?.maxSlippageBps ?? 100,
2383
2416
  ...configHash && { configHash }
2384
- });
2417
+ };
2418
+ if (this.vaultManager) {
2419
+ const vaultAddress = await this.vaultManager.getVaultAddress();
2420
+ if (vaultAddress && this.vaultManager.preferVaultTrading) {
2421
+ const routerTrade = await this.client.buildRouterTrade(tradeIntent);
2422
+ if (routerTrade.vaultParams) {
2423
+ console.log(`Attempting vault trade via ${vaultAddress}`);
2424
+ const vaultResult = await this.vaultManager.executeVaultTrade({
2425
+ tokenIn: signal.tokenIn,
2426
+ tokenOut: signal.tokenOut,
2427
+ amountIn: BigInt(signal.amountIn),
2428
+ minAmountOut: BigInt(routerTrade.minAmountOut),
2429
+ vaultParams: routerTrade.vaultParams
2430
+ });
2431
+ if (vaultResult) {
2432
+ if (vaultResult.txHash) {
2433
+ console.log(`Vault trade executed: ${vaultResult.txHash}`);
2434
+ return { success: true, txHash: vaultResult.txHash };
2435
+ }
2436
+ console.error(`Vault trade failed: ${vaultResult.error}`);
2437
+ return { success: false, error: vaultResult.error };
2438
+ }
2439
+ }
2440
+ }
2441
+ }
2442
+ const result = await this.client.trade(tradeIntent);
2385
2443
  console.log(`Trade executed: ${result.hash}`);
2386
2444
  return { success: true, txHash: result.hash };
2387
2445
  } catch (error) {
@@ -2867,9 +2925,10 @@ var VAULT_ABI = [
2867
2925
  outputs: [{ type: "uint256" }],
2868
2926
  stateMutability: "view"
2869
2927
  },
2928
+ // TOKEN → base asset (fee from output)
2870
2929
  {
2871
2930
  type: "function",
2872
- name: "executeTrade",
2931
+ name: "executeSwap",
2873
2932
  inputs: [
2874
2933
  { name: "tokenIn", type: "address" },
2875
2934
  { name: "tokenOut", type: "address" },
@@ -2877,6 +2936,44 @@ var VAULT_ABI = [
2877
2936
  { name: "minAmountOut", type: "uint256" },
2878
2937
  { name: "aggregator", type: "address" },
2879
2938
  { name: "swapData", type: "bytes" },
2939
+ { name: "feeConvertAggregator", type: "address" },
2940
+ { name: "feeSwapData", type: "bytes" },
2941
+ { name: "deadline", type: "uint256" }
2942
+ ],
2943
+ outputs: [{ type: "uint256" }],
2944
+ stateMutability: "nonpayable"
2945
+ },
2946
+ // Base asset → TOKEN (fee from input)
2947
+ {
2948
+ type: "function",
2949
+ name: "executeSwapSimple",
2950
+ inputs: [
2951
+ { name: "tokenIn", type: "address" },
2952
+ { name: "tokenOut", type: "address" },
2953
+ { name: "amountIn", type: "uint256" },
2954
+ { name: "minAmountOut", type: "uint256" },
2955
+ { name: "target", type: "address" },
2956
+ { name: "data", type: "bytes" },
2957
+ { name: "feeConvertAggregator", type: "address" },
2958
+ { name: "feeSwapData", type: "bytes" },
2959
+ { name: "deadline", type: "uint256" }
2960
+ ],
2961
+ outputs: [{ type: "uint256" }],
2962
+ stateMutability: "nonpayable"
2963
+ },
2964
+ // TOKEN → TOKEN (multi-hop via USDC, fee at midpoint)
2965
+ {
2966
+ type: "function",
2967
+ name: "executeSwapMultiHop",
2968
+ inputs: [
2969
+ { name: "tokenIn", type: "address" },
2970
+ { name: "tokenOut", type: "address" },
2971
+ { name: "amountIn", type: "uint256" },
2972
+ { name: "minAmountOut", type: "uint256" },
2973
+ { name: "aggregator1", type: "address" },
2974
+ { name: "swapData1", type: "bytes" },
2975
+ { name: "aggregator2", type: "address" },
2976
+ { name: "swapData2", type: "bytes" },
2880
2977
  { name: "deadline", type: "uint256" }
2881
2978
  ],
2882
2979
  outputs: [{ type: "uint256" }],
@@ -3142,8 +3239,9 @@ var VaultManager = class {
3142
3239
  }
3143
3240
  }
3144
3241
  /**
3145
- * Execute a trade through the vault (if it exists and policy allows)
3146
- * Returns null if should use direct trading instead
3242
+ * Execute a trade through the vault using API-provided vaultParams.
3243
+ * Dispatches to the correct vault function based on swapType.
3244
+ * Returns null if vault trading shouldn't be used (no vault, policy disabled).
3147
3245
  */
3148
3246
  async executeVaultTrade(params) {
3149
3247
  if (!this.preferVaultTrading) {
@@ -3154,23 +3252,71 @@ var VaultManager = class {
3154
3252
  return null;
3155
3253
  }
3156
3254
  const deadline = params.deadline || BigInt(Math.floor(Date.now() / 1e3) + 3600);
3255
+ const { vaultParams } = params;
3157
3256
  try {
3158
- const hash = await this.walletClient.writeContract({
3159
- address: vaultAddress,
3160
- abi: VAULT_ABI,
3161
- functionName: "executeTrade",
3162
- args: [
3163
- params.tokenIn,
3164
- params.tokenOut,
3165
- params.amountIn,
3166
- params.minAmountOut,
3167
- params.aggregator,
3168
- params.swapData,
3169
- deadline
3170
- ],
3171
- chain: import_chains2.base,
3172
- account: this.account
3173
- });
3257
+ let hash;
3258
+ switch (vaultParams.swapType) {
3259
+ case "swap":
3260
+ hash = await this.walletClient.writeContract({
3261
+ address: vaultAddress,
3262
+ abi: VAULT_ABI,
3263
+ functionName: "executeSwap",
3264
+ args: [
3265
+ params.tokenIn,
3266
+ params.tokenOut,
3267
+ params.amountIn,
3268
+ params.minAmountOut,
3269
+ vaultParams.aggregator,
3270
+ vaultParams.swapData,
3271
+ vaultParams.feeConvertAggregator,
3272
+ vaultParams.feeSwapData,
3273
+ deadline
3274
+ ],
3275
+ chain: import_chains2.base,
3276
+ account: this.account
3277
+ });
3278
+ break;
3279
+ case "simple":
3280
+ hash = await this.walletClient.writeContract({
3281
+ address: vaultAddress,
3282
+ abi: VAULT_ABI,
3283
+ functionName: "executeSwapSimple",
3284
+ args: [
3285
+ params.tokenIn,
3286
+ params.tokenOut,
3287
+ params.amountIn,
3288
+ params.minAmountOut,
3289
+ vaultParams.target,
3290
+ vaultParams.data,
3291
+ vaultParams.feeConvertAggregator,
3292
+ vaultParams.feeSwapData,
3293
+ deadline
3294
+ ],
3295
+ chain: import_chains2.base,
3296
+ account: this.account
3297
+ });
3298
+ break;
3299
+ case "multihop":
3300
+ hash = await this.walletClient.writeContract({
3301
+ address: vaultAddress,
3302
+ abi: VAULT_ABI,
3303
+ functionName: "executeSwapMultiHop",
3304
+ args: [
3305
+ params.tokenIn,
3306
+ params.tokenOut,
3307
+ params.amountIn,
3308
+ params.minAmountOut,
3309
+ vaultParams.aggregator1,
3310
+ vaultParams.swapData1,
3311
+ vaultParams.aggregator2,
3312
+ vaultParams.swapData2,
3313
+ deadline
3314
+ ],
3315
+ chain: import_chains2.base,
3316
+ account: this.account
3317
+ });
3318
+ break;
3319
+ }
3174
3320
  return { usedVault: true, txHash: hash };
3175
3321
  } catch (error) {
3176
3322
  return {
@@ -7148,6 +7294,8 @@ var AgentRuntime = class {
7148
7294
  processAlive = true;
7149
7295
  riskUniverse = 0;
7150
7296
  allowedTokens = /* @__PURE__ */ new Set();
7297
+ /** Frontier token discovery — cached addresses from Blockscout scan */
7298
+ discoveredTokens = [];
7151
7299
  strategyContext;
7152
7300
  positionTracker;
7153
7301
  // Paper trading components (null when not in paper mode)
@@ -7311,10 +7459,16 @@ var AgentRuntime = class {
7311
7459
  vaultConfig
7312
7460
  });
7313
7461
  console.log(`Vault policy: ${vaultConfig.policy}`);
7462
+ if (this.executor) {
7463
+ this.executor.setVaultManager(this.vaultManager);
7464
+ }
7314
7465
  const status = await this.vaultManager.getVaultStatus();
7315
7466
  if (status.hasVault) {
7316
7467
  console.log(`Vault exists: ${status.vaultAddress}`);
7317
7468
  console.log(`Vault TVL: ${Number(status.totalAssets) / 1e6} USDC`);
7469
+ if (vaultConfig.preferVaultTrading) {
7470
+ console.log("Vault trading enabled \u2014 trades will execute from vault");
7471
+ }
7318
7472
  } else {
7319
7473
  console.log("No vault exists for this agent");
7320
7474
  if (vaultConfig.policy === "manual") {
@@ -7749,6 +7903,13 @@ var AgentRuntime = class {
7749
7903
  console.log("");
7750
7904
  this.mode = "idle";
7751
7905
  try {
7906
+ if (this.riskUniverse === 4) {
7907
+ const discovered = await this.marketData.discoverTokenHoldings(this.client.address);
7908
+ if (discovered.length > 0) {
7909
+ this.discoveredTokens = discovered;
7910
+ console.log(`Frontier token discovery: ${discovered.length} token(s) found on-chain`);
7911
+ }
7912
+ }
7752
7913
  const tokens = this.getTokensToTrack();
7753
7914
  const initData = await this.marketData.fetchMarketData(this.client.address, tokens);
7754
7915
  void this.positionTracker.syncBalances(initData.balances, initData.prices);
@@ -8728,8 +8889,9 @@ This exit has been recorded in your trade history and counts toward your total P
8728
8889
  this.paperPortfolio.save();
8729
8890
  } else {
8730
8891
  const vaultStatus = await this.vaultManager?.getVaultStatus();
8731
- if (vaultStatus?.hasVault && this.vaultManager?.preferVaultTrading) {
8732
- console.log(`Trading through vault: ${vaultStatus.vaultAddress}`);
8892
+ if (vaultStatus?.hasVault) {
8893
+ const via = this.vaultManager?.preferVaultTrading ? "vault" : "wallet";
8894
+ console.log(`Vault active: ${vaultStatus.vaultAddress} (trades execute from ${via})`);
8733
8895
  }
8734
8896
  const results = await this.executor.executeAll(filteredSignals);
8735
8897
  let totalFeesUSD = 0;
@@ -9101,8 +9263,14 @@ See: https://exagent.io/docs/guides/manual-exit`,
9101
9263
  }
9102
9264
  }
9103
9265
  }
9266
+ for (const addr of this.discoveredTokens) {
9267
+ if (!baseSet.has(addr)) {
9268
+ extras.push(addr);
9269
+ baseSet.add(addr);
9270
+ }
9271
+ }
9104
9272
  if (extras.length > 0) {
9105
- console.log(`Auto-tracking ${extras.length} additional token(s) from position history`);
9273
+ console.log(`Auto-tracking ${extras.length} additional token(s) from position/discovery`);
9106
9274
  }
9107
9275
  const resolver = this.marketData.getResolver();
9108
9276
  const allTokens = [...base8, ...extras];
@@ -9442,7 +9610,7 @@ function loadSecureEnv(basePath, passphrase) {
9442
9610
  }
9443
9611
 
9444
9612
  // src/index.ts
9445
- var AGENT_VERSION = "0.1.45";
9613
+ var AGENT_VERSION = "0.1.47";
9446
9614
  // Annotate the CommonJS export names for ESM import in node:
9447
9615
  0 && (module.exports = {
9448
9616
  AGENT_VERSION,
package/dist/index.mjs CHANGED
@@ -62,7 +62,7 @@ import {
62
62
  tradeIdToBytes32,
63
63
  validateConfig,
64
64
  validateStrategy
65
- } from "./chunk-RR4RQDU3.mjs";
65
+ } from "./chunk-ICTDJ7VX.mjs";
66
66
  export {
67
67
  AGENT_VERSION,
68
68
  AgentConfigSchema,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exagent/agent",
3
- "version": "0.1.45",
3
+ "version": "0.1.47",
4
4
  "description": "Autonomous trading agent runtime for Exagent",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -29,7 +29,7 @@
29
29
  "clean": "rm -rf dist"
30
30
  },
31
31
  "dependencies": {
32
- "@exagent/sdk": "^0.1.20",
32
+ "@exagent/sdk": "^0.1.21",
33
33
  "@nktkas/hyperliquid": "^0.31.0",
34
34
  "@polymarket/clob-client": "^4.0.0",
35
35
  "chalk": "^5.3.0",