@exagent/agent 0.1.11 → 0.1.13
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/chunk-5NE2K3WU.mjs +2852 -0
- package/dist/chunk-BSNYL2DK.mjs +3005 -0
- package/dist/chunk-BZLZQCKQ.mjs +2853 -0
- package/dist/chunk-DZMU25QF.mjs +3021 -0
- package/dist/chunk-TBITVAFT.mjs +2780 -0
- package/dist/chunk-ZY3ZSYNN.mjs +3023 -0
- package/dist/cli.js +545 -267
- package/dist/cli.mjs +3 -3
- package/dist/index.d.mts +99 -47
- package/dist/index.d.ts +99 -47
- package/dist/index.js +547 -267
- package/dist/index.mjs +1 -1
- package/package.json +2 -2
- package/templates/.env.template +3 -4
- package/templates/docker-compose.yml +1 -1
- package/templates/strategy.template.ts +1 -1
package/dist/cli.js
CHANGED
|
@@ -31,7 +31,7 @@ var fs2 = __toESM(require("fs"));
|
|
|
31
31
|
var path2 = __toESM(require("path"));
|
|
32
32
|
|
|
33
33
|
// src/runtime.ts
|
|
34
|
-
var
|
|
34
|
+
var import_sdk2 = require("@exagent/sdk");
|
|
35
35
|
var import_viem3 = require("viem");
|
|
36
36
|
var import_chains2 = require("viem/chains");
|
|
37
37
|
|
|
@@ -779,7 +779,7 @@ export const generateSignals: StrategyFunction = async (marketData, llm, config)
|
|
|
779
779
|
riskWarnings: [
|
|
780
780
|
"Custom strategies have no guardrails - you are fully responsible",
|
|
781
781
|
"LLMs can hallucinate or make errors - always validate outputs",
|
|
782
|
-
"
|
|
782
|
+
"Start with small amounts before scaling up",
|
|
783
783
|
"Consider edge cases: what happens if the LLM returns invalid JSON?",
|
|
784
784
|
"Your prompts and strategy logic are your competitive advantage - protect them",
|
|
785
785
|
"Agents may not behave exactly as expected based on your prompts"
|
|
@@ -838,9 +838,13 @@ function getAllStrategyTemplates() {
|
|
|
838
838
|
var TradeExecutor = class {
|
|
839
839
|
client;
|
|
840
840
|
config;
|
|
841
|
+
allowedTokens;
|
|
841
842
|
constructor(client, config) {
|
|
842
843
|
this.client = client;
|
|
843
844
|
this.config = config;
|
|
845
|
+
this.allowedTokens = new Set(
|
|
846
|
+
(config.allowedTokens || []).map((t) => t.toLowerCase())
|
|
847
|
+
);
|
|
844
848
|
}
|
|
845
849
|
/**
|
|
846
850
|
* Execute a single trade signal
|
|
@@ -859,8 +863,7 @@ var TradeExecutor = class {
|
|
|
859
863
|
tokenIn: signal.tokenIn,
|
|
860
864
|
tokenOut: signal.tokenOut,
|
|
861
865
|
amountIn: signal.amountIn,
|
|
862
|
-
maxSlippageBps: 100
|
|
863
|
-
// 1% default slippage
|
|
866
|
+
maxSlippageBps: this.config.trading?.maxSlippageBps ?? 100
|
|
864
867
|
});
|
|
865
868
|
console.log(`Trade executed: ${result.hash}`);
|
|
866
869
|
return { success: true, txHash: result.hash };
|
|
@@ -886,13 +889,25 @@ var TradeExecutor = class {
|
|
|
886
889
|
return results;
|
|
887
890
|
}
|
|
888
891
|
/**
|
|
889
|
-
* Validate a signal against config limits
|
|
892
|
+
* Validate a signal against config limits and token restrictions
|
|
890
893
|
*/
|
|
891
894
|
validateSignal(signal) {
|
|
892
895
|
if (signal.confidence < 0.5) {
|
|
893
896
|
console.warn(`Signal confidence ${signal.confidence} below threshold (0.5)`);
|
|
894
897
|
return false;
|
|
895
898
|
}
|
|
899
|
+
if (this.allowedTokens.size > 0) {
|
|
900
|
+
const tokenInAllowed = this.allowedTokens.has(signal.tokenIn.toLowerCase());
|
|
901
|
+
const tokenOutAllowed = this.allowedTokens.has(signal.tokenOut.toLowerCase());
|
|
902
|
+
if (!tokenInAllowed) {
|
|
903
|
+
console.warn(`Token ${signal.tokenIn} not in allowed list for this agent's risk universe \u2014 skipping`);
|
|
904
|
+
return false;
|
|
905
|
+
}
|
|
906
|
+
if (!tokenOutAllowed) {
|
|
907
|
+
console.warn(`Token ${signal.tokenOut} not in allowed list for this agent's risk universe \u2014 skipping`);
|
|
908
|
+
return false;
|
|
909
|
+
}
|
|
910
|
+
}
|
|
896
911
|
return true;
|
|
897
912
|
}
|
|
898
913
|
delay(ms) {
|
|
@@ -900,103 +915,99 @@ var TradeExecutor = class {
|
|
|
900
915
|
}
|
|
901
916
|
};
|
|
902
917
|
|
|
903
|
-
// src/trading/risk.ts
|
|
904
|
-
var RiskManager = class {
|
|
905
|
-
config;
|
|
906
|
-
dailyPnL = 0;
|
|
907
|
-
lastResetDate = "";
|
|
908
|
-
constructor(config) {
|
|
909
|
-
this.config = config;
|
|
910
|
-
}
|
|
911
|
-
/**
|
|
912
|
-
* Filter signals through risk checks
|
|
913
|
-
* Returns only signals that pass all guardrails
|
|
914
|
-
*/
|
|
915
|
-
filterSignals(signals, marketData) {
|
|
916
|
-
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
917
|
-
if (today !== this.lastResetDate) {
|
|
918
|
-
this.dailyPnL = 0;
|
|
919
|
-
this.lastResetDate = today;
|
|
920
|
-
}
|
|
921
|
-
if (this.isDailyLossLimitHit(marketData.portfolioValue)) {
|
|
922
|
-
console.warn("Daily loss limit reached - no new trades");
|
|
923
|
-
return [];
|
|
924
|
-
}
|
|
925
|
-
return signals.filter((signal) => this.validateSignal(signal, marketData));
|
|
926
|
-
}
|
|
927
|
-
/**
|
|
928
|
-
* Validate individual signal against risk limits
|
|
929
|
-
*/
|
|
930
|
-
validateSignal(signal, marketData) {
|
|
931
|
-
if (signal.action === "hold") {
|
|
932
|
-
return true;
|
|
933
|
-
}
|
|
934
|
-
const signalValue = this.estimateSignalValue(signal, marketData);
|
|
935
|
-
const maxPositionValue = marketData.portfolioValue * this.config.maxPositionSizeBps / 1e4;
|
|
936
|
-
if (signalValue > maxPositionValue) {
|
|
937
|
-
console.warn(
|
|
938
|
-
`Signal exceeds position limit: ${signalValue.toFixed(2)} > ${maxPositionValue.toFixed(2)}`
|
|
939
|
-
);
|
|
940
|
-
return false;
|
|
941
|
-
}
|
|
942
|
-
if (signal.confidence < 0.5) {
|
|
943
|
-
console.warn(`Signal confidence too low: ${signal.confidence}`);
|
|
944
|
-
return false;
|
|
945
|
-
}
|
|
946
|
-
return true;
|
|
947
|
-
}
|
|
948
|
-
/**
|
|
949
|
-
* Check if daily loss limit has been hit
|
|
950
|
-
*/
|
|
951
|
-
isDailyLossLimitHit(portfolioValue) {
|
|
952
|
-
const maxLoss = portfolioValue * this.config.maxDailyLossBps / 1e4;
|
|
953
|
-
return this.dailyPnL < -maxLoss;
|
|
954
|
-
}
|
|
955
|
-
/**
|
|
956
|
-
* Estimate USD value of a trade signal
|
|
957
|
-
*/
|
|
958
|
-
estimateSignalValue(signal, marketData) {
|
|
959
|
-
const price = marketData.prices[signal.tokenIn] || 0;
|
|
960
|
-
const amount = Number(signal.amountIn) / 1e18;
|
|
961
|
-
return amount * price;
|
|
962
|
-
}
|
|
963
|
-
/**
|
|
964
|
-
* Update daily PnL after a trade
|
|
965
|
-
*/
|
|
966
|
-
updatePnL(pnl) {
|
|
967
|
-
this.dailyPnL += pnl;
|
|
968
|
-
}
|
|
969
|
-
/**
|
|
970
|
-
* Get current risk status
|
|
971
|
-
*/
|
|
972
|
-
getStatus() {
|
|
973
|
-
return {
|
|
974
|
-
dailyPnL: this.dailyPnL,
|
|
975
|
-
dailyLossLimit: this.config.maxDailyLossBps / 100,
|
|
976
|
-
// As percentage
|
|
977
|
-
isLimitHit: this.dailyPnL < -(this.config.maxDailyLossBps / 100)
|
|
978
|
-
};
|
|
979
|
-
}
|
|
980
|
-
};
|
|
981
|
-
|
|
982
918
|
// src/trading/market.ts
|
|
983
919
|
var import_viem = require("viem");
|
|
984
920
|
var NATIVE_ETH = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";
|
|
985
|
-
var
|
|
921
|
+
var TOKEN_DECIMALS = {
|
|
922
|
+
// Base Mainnet — Core tokens
|
|
986
923
|
"0x833589fcd6edb6e08f4c7c32d4f71b54bda02913": 6,
|
|
987
|
-
// USDC
|
|
988
|
-
"0xd9aaec86b65d86f6a7b5b1b0c42ffa531710b6ca": 6
|
|
989
|
-
// USDbC
|
|
924
|
+
// USDC
|
|
925
|
+
"0xd9aaec86b65d86f6a7b5b1b0c42ffa531710b6ca": 6,
|
|
926
|
+
// USDbC
|
|
927
|
+
"0x4200000000000000000000000000000000000006": 18,
|
|
928
|
+
// WETH
|
|
929
|
+
"0x50c5725949a6f0c72e6c4a641f24049a917db0cb": 18,
|
|
930
|
+
// DAI
|
|
931
|
+
"0x2ae3f1ec7f1f5012cfeab0185bfc7aa3cf0dec22": 18,
|
|
932
|
+
// cbETH
|
|
933
|
+
[NATIVE_ETH.toLowerCase()]: 18,
|
|
934
|
+
// Native ETH
|
|
935
|
+
// Base Mainnet — Established tokens
|
|
936
|
+
"0x940181a94a35a4569e4529a3cdfb74e38fd98631": 18,
|
|
937
|
+
// AERO (Aerodrome)
|
|
938
|
+
"0x532f27101965dd16442e59d40670faf5ebb142e4": 18,
|
|
939
|
+
// BRETT
|
|
940
|
+
"0x4ed4e862860bed51a9570b96d89af5e1b0efefed": 18,
|
|
941
|
+
// DEGEN
|
|
942
|
+
"0x0b3e328455c4059eeb9e3f84b5543f74e24e7e1b": 18,
|
|
943
|
+
// VIRTUAL
|
|
944
|
+
"0xac1bd2486aaf3b5c0fc3fd868558b082a531b2b4": 18,
|
|
945
|
+
// TOSHI
|
|
946
|
+
"0xcbb7c0000ab88b473b1f5afd9ef808440eed33bf": 8,
|
|
947
|
+
// cbBTC
|
|
948
|
+
"0x2416092f143378750bb29b79ed961ab195cceea5": 18,
|
|
949
|
+
// ezETH (Renzo)
|
|
950
|
+
"0xc1cba3fcea344f92d9239c08c0568f6f2f0ee452": 18
|
|
951
|
+
// wstETH (Lido)
|
|
990
952
|
};
|
|
953
|
+
function getTokenDecimals(address) {
|
|
954
|
+
const decimals = TOKEN_DECIMALS[address.toLowerCase()];
|
|
955
|
+
if (decimals === void 0) {
|
|
956
|
+
console.warn(`Unknown token decimals for ${address}, defaulting to 18. THIS MAY BE WRONG.`);
|
|
957
|
+
return 18;
|
|
958
|
+
}
|
|
959
|
+
return decimals;
|
|
960
|
+
}
|
|
961
|
+
var TOKEN_TO_COINGECKO = {
|
|
962
|
+
// Core
|
|
963
|
+
"0x4200000000000000000000000000000000000006": "ethereum",
|
|
964
|
+
// WETH
|
|
965
|
+
[NATIVE_ETH.toLowerCase()]: "ethereum",
|
|
966
|
+
"0x833589fcd6edb6e08f4c7c32d4f71b54bda02913": "usd-coin",
|
|
967
|
+
// USDC
|
|
968
|
+
"0xd9aaec86b65d86f6a7b5b1b0c42ffa531710b6ca": "usd-coin",
|
|
969
|
+
// USDbC
|
|
970
|
+
"0x2ae3f1ec7f1f5012cfeab0185bfc7aa3cf0dec22": "coinbase-wrapped-staked-eth",
|
|
971
|
+
// cbETH
|
|
972
|
+
"0x50c5725949a6f0c72e6c4a641f24049a917db0cb": "dai",
|
|
973
|
+
// DAI
|
|
974
|
+
// Established
|
|
975
|
+
"0x940181a94a35a4569e4529a3cdfb74e38fd98631": "aerodrome-finance",
|
|
976
|
+
// AERO
|
|
977
|
+
"0x532f27101965dd16442e59d40670faf5ebb142e4": "brett",
|
|
978
|
+
// BRETT
|
|
979
|
+
"0x4ed4e862860bed51a9570b96d89af5e1b0efefed": "degen-base",
|
|
980
|
+
// DEGEN
|
|
981
|
+
"0x0b3e328455c4059eeb9e3f84b5543f74e24e7e1b": "virtual-protocol",
|
|
982
|
+
// VIRTUAL
|
|
983
|
+
"0xac1bd2486aaf3b5c0fc3fd868558b082a531b2b4": "toshi",
|
|
984
|
+
// TOSHI
|
|
985
|
+
"0xcbb7c0000ab88b473b1f5afd9ef808440eed33bf": "coinbase-wrapped-btc",
|
|
986
|
+
// cbBTC
|
|
987
|
+
"0x2416092f143378750bb29b79ed961ab195cceea5": "renzo-restaked-eth",
|
|
988
|
+
// ezETH
|
|
989
|
+
"0xc1cba3fcea344f92d9239c08c0568f6f2f0ee452": "wrapped-steth"
|
|
990
|
+
// wstETH
|
|
991
|
+
};
|
|
992
|
+
var STABLECOIN_IDS = /* @__PURE__ */ new Set(["usd-coin", "dai"]);
|
|
993
|
+
var PRICE_STALENESS_MS = 6e4;
|
|
991
994
|
var MarketDataService = class {
|
|
992
995
|
rpcUrl;
|
|
993
996
|
client;
|
|
997
|
+
/** Cached prices from last fetch */
|
|
998
|
+
cachedPrices = {};
|
|
999
|
+
/** Timestamp of last successful price fetch */
|
|
1000
|
+
lastPriceFetchAt = 0;
|
|
994
1001
|
constructor(rpcUrl) {
|
|
995
1002
|
this.rpcUrl = rpcUrl;
|
|
996
1003
|
this.client = (0, import_viem.createPublicClient)({
|
|
997
1004
|
transport: (0, import_viem.http)(rpcUrl)
|
|
998
1005
|
});
|
|
999
1006
|
}
|
|
1007
|
+
/** Cached volume data */
|
|
1008
|
+
cachedVolume24h = {};
|
|
1009
|
+
/** Cached price change data */
|
|
1010
|
+
cachedPriceChange24h = {};
|
|
1000
1011
|
/**
|
|
1001
1012
|
* Fetch current market data for the agent
|
|
1002
1013
|
*/
|
|
@@ -1004,31 +1015,126 @@ var MarketDataService = class {
|
|
|
1004
1015
|
const prices = await this.fetchPrices(tokenAddresses);
|
|
1005
1016
|
const balances = await this.fetchBalances(walletAddress, tokenAddresses);
|
|
1006
1017
|
const portfolioValue = this.calculatePortfolioValue(balances, prices);
|
|
1018
|
+
let gasPrice;
|
|
1019
|
+
try {
|
|
1020
|
+
gasPrice = await this.client.getGasPrice();
|
|
1021
|
+
} catch {
|
|
1022
|
+
}
|
|
1007
1023
|
return {
|
|
1008
1024
|
timestamp: Date.now(),
|
|
1009
1025
|
prices,
|
|
1010
1026
|
balances,
|
|
1011
|
-
portfolioValue
|
|
1027
|
+
portfolioValue,
|
|
1028
|
+
volume24h: Object.keys(this.cachedVolume24h).length > 0 ? { ...this.cachedVolume24h } : void 0,
|
|
1029
|
+
priceChange24h: Object.keys(this.cachedPriceChange24h).length > 0 ? { ...this.cachedPriceChange24h } : void 0,
|
|
1030
|
+
gasPrice,
|
|
1031
|
+
network: {
|
|
1032
|
+
chainId: this.client.chain?.id ?? 8453
|
|
1033
|
+
}
|
|
1012
1034
|
};
|
|
1013
1035
|
}
|
|
1014
1036
|
/**
|
|
1015
|
-
*
|
|
1037
|
+
* Check if cached prices are still fresh
|
|
1038
|
+
*/
|
|
1039
|
+
get pricesAreFresh() {
|
|
1040
|
+
return Date.now() - this.lastPriceFetchAt < PRICE_STALENESS_MS;
|
|
1041
|
+
}
|
|
1042
|
+
/**
|
|
1043
|
+
* Fetch token prices from CoinGecko free API
|
|
1044
|
+
* Returns cached prices if still fresh (<60s old)
|
|
1016
1045
|
*/
|
|
1017
1046
|
async fetchPrices(tokenAddresses) {
|
|
1047
|
+
if (this.pricesAreFresh && Object.keys(this.cachedPrices).length > 0) {
|
|
1048
|
+
const prices2 = { ...this.cachedPrices };
|
|
1049
|
+
for (const addr of tokenAddresses) {
|
|
1050
|
+
const cgId = TOKEN_TO_COINGECKO[addr.toLowerCase()];
|
|
1051
|
+
if (cgId && STABLECOIN_IDS.has(cgId) && !prices2[addr.toLowerCase()]) {
|
|
1052
|
+
prices2[addr.toLowerCase()] = 1;
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
return prices2;
|
|
1056
|
+
}
|
|
1018
1057
|
const prices = {};
|
|
1019
|
-
const
|
|
1020
|
-
|
|
1021
|
-
[
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1058
|
+
const idsToFetch = /* @__PURE__ */ new Set();
|
|
1059
|
+
for (const addr of tokenAddresses) {
|
|
1060
|
+
const cgId = TOKEN_TO_COINGECKO[addr.toLowerCase()];
|
|
1061
|
+
if (cgId && !STABLECOIN_IDS.has(cgId)) {
|
|
1062
|
+
idsToFetch.add(cgId);
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
idsToFetch.add("ethereum");
|
|
1066
|
+
if (idsToFetch.size > 0) {
|
|
1067
|
+
try {
|
|
1068
|
+
const ids = Array.from(idsToFetch).join(",");
|
|
1069
|
+
const response = await fetch(
|
|
1070
|
+
`https://api.coingecko.com/api/v3/simple/price?ids=${ids}&vs_currencies=usd&include_24hr_vol=true&include_24hr_change=true`,
|
|
1071
|
+
{ signal: AbortSignal.timeout(5e3) }
|
|
1072
|
+
);
|
|
1073
|
+
if (response.ok) {
|
|
1074
|
+
const data = await response.json();
|
|
1075
|
+
for (const [cgId, priceData] of Object.entries(data)) {
|
|
1076
|
+
for (const [addr, id] of Object.entries(TOKEN_TO_COINGECKO)) {
|
|
1077
|
+
if (id === cgId) {
|
|
1078
|
+
const key = addr.toLowerCase();
|
|
1079
|
+
prices[key] = priceData.usd;
|
|
1080
|
+
if (priceData.usd_24h_vol !== void 0) {
|
|
1081
|
+
this.cachedVolume24h[key] = priceData.usd_24h_vol;
|
|
1082
|
+
}
|
|
1083
|
+
if (priceData.usd_24h_change !== void 0) {
|
|
1084
|
+
this.cachedPriceChange24h[key] = priceData.usd_24h_change;
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
this.lastPriceFetchAt = Date.now();
|
|
1090
|
+
} else {
|
|
1091
|
+
console.warn(`CoinGecko API returned ${response.status}, using cached prices`);
|
|
1092
|
+
}
|
|
1093
|
+
} catch (error) {
|
|
1094
|
+
console.warn("Failed to fetch prices from CoinGecko:", error instanceof Error ? error.message : error);
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
for (const addr of tokenAddresses) {
|
|
1098
|
+
const cgId = TOKEN_TO_COINGECKO[addr.toLowerCase()];
|
|
1099
|
+
if (cgId && STABLECOIN_IDS.has(cgId)) {
|
|
1100
|
+
prices[addr.toLowerCase()] = 1;
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
const missingAddrs = tokenAddresses.filter(
|
|
1104
|
+
(addr) => !prices[addr.toLowerCase()] && !STABLECOIN_IDS.has(TOKEN_TO_COINGECKO[addr.toLowerCase()] || "")
|
|
1105
|
+
);
|
|
1106
|
+
if (missingAddrs.length > 0) {
|
|
1107
|
+
try {
|
|
1108
|
+
const coins = missingAddrs.map((a) => `base:${a}`).join(",");
|
|
1109
|
+
const llamaResponse = await fetch(
|
|
1110
|
+
`https://coins.llama.fi/prices/current/${coins}`,
|
|
1111
|
+
{ signal: AbortSignal.timeout(5e3) }
|
|
1112
|
+
);
|
|
1113
|
+
if (llamaResponse.ok) {
|
|
1114
|
+
const llamaData = await llamaResponse.json();
|
|
1115
|
+
for (const [key, data] of Object.entries(llamaData.coins)) {
|
|
1116
|
+
const addr = key.replace("base:", "").toLowerCase();
|
|
1117
|
+
if (data.price && data.confidence > 0.5) {
|
|
1118
|
+
prices[addr] = data.price;
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
if (!this.lastPriceFetchAt) this.lastPriceFetchAt = Date.now();
|
|
1122
|
+
}
|
|
1123
|
+
} catch {
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
if (Object.keys(prices).length > 0) {
|
|
1127
|
+
this.cachedPrices = prices;
|
|
1128
|
+
}
|
|
1129
|
+
if (Object.keys(prices).length === 0 && Object.keys(this.cachedPrices).length > 0) {
|
|
1130
|
+
console.warn("Using cached prices (last successful fetch was stale)");
|
|
1131
|
+
return { ...this.cachedPrices };
|
|
1132
|
+
}
|
|
1133
|
+
for (const addr of tokenAddresses) {
|
|
1134
|
+
if (!prices[addr.toLowerCase()]) {
|
|
1135
|
+
console.warn(`No price available for ${addr}, using 0`);
|
|
1136
|
+
prices[addr.toLowerCase()] = 0;
|
|
1137
|
+
}
|
|
1032
1138
|
}
|
|
1033
1139
|
return prices;
|
|
1034
1140
|
}
|
|
@@ -1074,7 +1180,7 @@ var MarketDataService = class {
|
|
|
1074
1180
|
let total = 0;
|
|
1075
1181
|
for (const [address, balance] of Object.entries(balances)) {
|
|
1076
1182
|
const price = prices[address.toLowerCase()] || 0;
|
|
1077
|
-
const decimals =
|
|
1183
|
+
const decimals = getTokenDecimals(address);
|
|
1078
1184
|
const amount = Number(balance) / Math.pow(10, decimals);
|
|
1079
1185
|
total += amount * price;
|
|
1080
1186
|
}
|
|
@@ -1082,22 +1188,138 @@ var MarketDataService = class {
|
|
|
1082
1188
|
}
|
|
1083
1189
|
};
|
|
1084
1190
|
|
|
1191
|
+
// src/trading/risk.ts
|
|
1192
|
+
var RiskManager = class {
|
|
1193
|
+
config;
|
|
1194
|
+
dailyPnL = 0;
|
|
1195
|
+
dailyFees = 0;
|
|
1196
|
+
lastResetDate = "";
|
|
1197
|
+
/** Minimum trade value in USD — trades below this are rejected as dust */
|
|
1198
|
+
minTradeValueUSD;
|
|
1199
|
+
constructor(config) {
|
|
1200
|
+
this.config = config;
|
|
1201
|
+
this.minTradeValueUSD = config.minTradeValueUSD ?? 1;
|
|
1202
|
+
}
|
|
1203
|
+
/**
|
|
1204
|
+
* Filter signals through risk checks
|
|
1205
|
+
* Returns only signals that pass all guardrails
|
|
1206
|
+
*/
|
|
1207
|
+
filterSignals(signals, marketData) {
|
|
1208
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
1209
|
+
if (today !== this.lastResetDate) {
|
|
1210
|
+
this.dailyPnL = 0;
|
|
1211
|
+
this.dailyFees = 0;
|
|
1212
|
+
this.lastResetDate = today;
|
|
1213
|
+
}
|
|
1214
|
+
if (this.isDailyLossLimitHit(marketData.portfolioValue)) {
|
|
1215
|
+
console.warn("Daily loss limit reached - no new trades");
|
|
1216
|
+
return [];
|
|
1217
|
+
}
|
|
1218
|
+
return signals.filter((signal) => this.validateSignal(signal, marketData));
|
|
1219
|
+
}
|
|
1220
|
+
/**
|
|
1221
|
+
* Validate individual signal against risk limits
|
|
1222
|
+
*/
|
|
1223
|
+
validateSignal(signal, marketData) {
|
|
1224
|
+
if (signal.action === "hold") {
|
|
1225
|
+
return true;
|
|
1226
|
+
}
|
|
1227
|
+
const signalValue = this.estimateSignalValue(signal, marketData);
|
|
1228
|
+
const maxPositionValue = marketData.portfolioValue * this.config.maxPositionSizeBps / 1e4;
|
|
1229
|
+
if (signalValue > maxPositionValue) {
|
|
1230
|
+
console.warn(
|
|
1231
|
+
`Signal exceeds position limit: ${signalValue.toFixed(2)} > ${maxPositionValue.toFixed(2)}`
|
|
1232
|
+
);
|
|
1233
|
+
return false;
|
|
1234
|
+
}
|
|
1235
|
+
if (signal.confidence < 0.5) {
|
|
1236
|
+
console.warn(`Signal confidence too low: ${signal.confidence}`);
|
|
1237
|
+
return false;
|
|
1238
|
+
}
|
|
1239
|
+
if (signal.action === "buy" && this.config.maxConcurrentPositions) {
|
|
1240
|
+
const activePositions = this.countActivePositions(marketData);
|
|
1241
|
+
if (activePositions >= this.config.maxConcurrentPositions) {
|
|
1242
|
+
console.warn(
|
|
1243
|
+
`Max concurrent positions reached: ${activePositions}/${this.config.maxConcurrentPositions} \u2014 blocking new buy`
|
|
1244
|
+
);
|
|
1245
|
+
return false;
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
if (signalValue < this.minTradeValueUSD) {
|
|
1249
|
+
console.warn(`Trade value $${signalValue.toFixed(2)} below minimum $${this.minTradeValueUSD} \u2014 skipping`);
|
|
1250
|
+
return false;
|
|
1251
|
+
}
|
|
1252
|
+
return true;
|
|
1253
|
+
}
|
|
1254
|
+
/**
|
|
1255
|
+
* Count non-zero token positions (excluding native ETH and stablecoins used as base currency)
|
|
1256
|
+
*/
|
|
1257
|
+
countActivePositions(marketData) {
|
|
1258
|
+
const NATIVE_ETH_KEY = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee";
|
|
1259
|
+
let count = 0;
|
|
1260
|
+
for (const [address, balance] of Object.entries(marketData.balances)) {
|
|
1261
|
+
if (address.toLowerCase() === NATIVE_ETH_KEY) continue;
|
|
1262
|
+
if (balance > 0n) count++;
|
|
1263
|
+
}
|
|
1264
|
+
return count;
|
|
1265
|
+
}
|
|
1266
|
+
/**
|
|
1267
|
+
* Check if daily loss limit has been hit
|
|
1268
|
+
*/
|
|
1269
|
+
isDailyLossLimitHit(portfolioValue) {
|
|
1270
|
+
const maxLoss = portfolioValue * this.config.maxDailyLossBps / 1e4;
|
|
1271
|
+
return this.dailyPnL < -maxLoss;
|
|
1272
|
+
}
|
|
1273
|
+
/**
|
|
1274
|
+
* Estimate USD value of a trade signal
|
|
1275
|
+
*/
|
|
1276
|
+
estimateSignalValue(signal, marketData) {
|
|
1277
|
+
const price = marketData.prices[signal.tokenIn.toLowerCase()] || 0;
|
|
1278
|
+
const tokenDecimals = getTokenDecimals(signal.tokenIn);
|
|
1279
|
+
const amount = Number(signal.amountIn) / Math.pow(10, tokenDecimals);
|
|
1280
|
+
return amount * price;
|
|
1281
|
+
}
|
|
1282
|
+
/**
|
|
1283
|
+
* Update daily PnL after a trade (market gains/losses only)
|
|
1284
|
+
*/
|
|
1285
|
+
updatePnL(pnl) {
|
|
1286
|
+
this.dailyPnL += pnl;
|
|
1287
|
+
}
|
|
1288
|
+
/**
|
|
1289
|
+
* Update daily fees (trading fees, gas costs, etc.)
|
|
1290
|
+
* Fees are tracked separately and do NOT count toward the daily loss limit.
|
|
1291
|
+
* This prevents protocol fees from triggering circuit breakers.
|
|
1292
|
+
*/
|
|
1293
|
+
updateFees(fees) {
|
|
1294
|
+
this.dailyFees += fees;
|
|
1295
|
+
}
|
|
1296
|
+
/**
|
|
1297
|
+
* Get current risk status
|
|
1298
|
+
* @param portfolioValue - Current portfolio value in USD (needed for accurate loss limit)
|
|
1299
|
+
*/
|
|
1300
|
+
getStatus(portfolioValue) {
|
|
1301
|
+
const pv = portfolioValue || 0;
|
|
1302
|
+
const maxLossUSD = pv * this.config.maxDailyLossBps / 1e4;
|
|
1303
|
+
return {
|
|
1304
|
+
dailyPnL: this.dailyPnL,
|
|
1305
|
+
dailyFees: this.dailyFees,
|
|
1306
|
+
dailyNetPnL: this.dailyPnL - this.dailyFees,
|
|
1307
|
+
dailyLossLimit: maxLossUSD,
|
|
1308
|
+
// Only market PnL triggers the limit — fees are excluded
|
|
1309
|
+
isLimitHit: pv > 0 ? this.dailyPnL < -maxLossUSD : false
|
|
1310
|
+
};
|
|
1311
|
+
}
|
|
1312
|
+
};
|
|
1313
|
+
|
|
1085
1314
|
// src/vault/manager.ts
|
|
1086
1315
|
var import_viem2 = require("viem");
|
|
1087
1316
|
var import_accounts = require("viem/accounts");
|
|
1088
1317
|
var import_chains = require("viem/chains");
|
|
1089
1318
|
var ADDRESSES = {
|
|
1090
|
-
testnet: {
|
|
1091
|
-
vaultFactory: "0x5c099daaE33801a907Bb57011c6749655b55dc75",
|
|
1092
|
-
registry: "0xCF48C341e3FebeCA5ECB7eb2535f61A2Ba855d9C",
|
|
1093
|
-
usdc: "0x036CbD53842c5426634e7929541eC2318f3dCF7e"
|
|
1094
|
-
},
|
|
1095
1319
|
mainnet: {
|
|
1096
|
-
vaultFactory: "0x0000000000000000000000000000000000000000",
|
|
1097
|
-
|
|
1098
|
-
registry: "0x0000000000000000000000000000000000000000",
|
|
1320
|
+
vaultFactory: process.env.EXAGENT_VAULT_FACTORY_ADDRESS || "0x0000000000000000000000000000000000000000",
|
|
1321
|
+
registry: process.env.EXAGENT_REGISTRY_ADDRESS || "0x0000000000000000000000000000000000000000",
|
|
1099
1322
|
usdc: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
|
|
1100
|
-
// Base mainnet USDC
|
|
1101
1323
|
}
|
|
1102
1324
|
};
|
|
1103
1325
|
var VAULT_FACTORY_ABI = [
|
|
@@ -1121,6 +1343,7 @@ var VAULT_FACTORY_ABI = [
|
|
|
1121
1343
|
inputs: [
|
|
1122
1344
|
{ name: "agentId", type: "uint256" },
|
|
1123
1345
|
{ name: "asset", type: "address" },
|
|
1346
|
+
{ name: "seedAmount", type: "uint256" },
|
|
1124
1347
|
{ name: "name", type: "string" },
|
|
1125
1348
|
{ name: "symbol", type: "string" },
|
|
1126
1349
|
{ name: "feeRecipient", type: "address" }
|
|
@@ -1134,13 +1357,6 @@ var VAULT_FACTORY_ABI = [
|
|
|
1134
1357
|
inputs: [],
|
|
1135
1358
|
outputs: [{ type: "uint256" }],
|
|
1136
1359
|
stateMutability: "view"
|
|
1137
|
-
},
|
|
1138
|
-
{
|
|
1139
|
-
type: "function",
|
|
1140
|
-
name: "eXABurnFee",
|
|
1141
|
-
inputs: [],
|
|
1142
|
-
outputs: [{ type: "uint256" }],
|
|
1143
|
-
stateMutability: "view"
|
|
1144
1360
|
}
|
|
1145
1361
|
];
|
|
1146
1362
|
var VAULT_ABI = [
|
|
@@ -1180,12 +1396,13 @@ var VaultManager = class {
|
|
|
1180
1396
|
lastVaultCheck = 0;
|
|
1181
1397
|
VAULT_CACHE_TTL = 6e4;
|
|
1182
1398
|
// 1 minute
|
|
1399
|
+
enabled = true;
|
|
1183
1400
|
constructor(config) {
|
|
1184
1401
|
this.config = config;
|
|
1185
1402
|
this.addresses = ADDRESSES[config.network];
|
|
1186
1403
|
this.account = (0, import_accounts.privateKeyToAccount)(config.walletKey);
|
|
1187
|
-
this.chain =
|
|
1188
|
-
const rpcUrl =
|
|
1404
|
+
this.chain = import_chains.base;
|
|
1405
|
+
const rpcUrl = "https://mainnet.base.org";
|
|
1189
1406
|
this.publicClient = (0, import_viem2.createPublicClient)({
|
|
1190
1407
|
chain: this.chain,
|
|
1191
1408
|
transport: (0, import_viem2.http)(rpcUrl)
|
|
@@ -1195,6 +1412,10 @@ var VaultManager = class {
|
|
|
1195
1412
|
chain: this.chain,
|
|
1196
1413
|
transport: (0, import_viem2.http)(rpcUrl)
|
|
1197
1414
|
});
|
|
1415
|
+
if (this.addresses.vaultFactory === "0x0000000000000000000000000000000000000000") {
|
|
1416
|
+
console.warn("VaultFactory address is zero \u2014 vault operations will be disabled");
|
|
1417
|
+
this.enabled = false;
|
|
1418
|
+
}
|
|
1198
1419
|
}
|
|
1199
1420
|
/**
|
|
1200
1421
|
* Get the agent's vault policy
|
|
@@ -1212,6 +1433,17 @@ var VaultManager = class {
|
|
|
1212
1433
|
* Get comprehensive vault status
|
|
1213
1434
|
*/
|
|
1214
1435
|
async getVaultStatus() {
|
|
1436
|
+
if (!this.enabled) {
|
|
1437
|
+
return {
|
|
1438
|
+
hasVault: false,
|
|
1439
|
+
vaultAddress: null,
|
|
1440
|
+
totalAssets: BigInt(0),
|
|
1441
|
+
canCreateVault: false,
|
|
1442
|
+
cannotCreateReason: "Vault operations disabled (contract address not set)",
|
|
1443
|
+
requirementsMet: false,
|
|
1444
|
+
requirements: { veXARequired: BigInt(0), isBypassed: false }
|
|
1445
|
+
};
|
|
1446
|
+
}
|
|
1215
1447
|
const vaultAddress = await this.getVaultAddress();
|
|
1216
1448
|
const hasVault = vaultAddress !== null;
|
|
1217
1449
|
let totalAssets = BigInt(0);
|
|
@@ -1246,22 +1478,16 @@ var VaultManager = class {
|
|
|
1246
1478
|
}
|
|
1247
1479
|
/**
|
|
1248
1480
|
* Get vault creation requirements
|
|
1481
|
+
* Note: No burnFee on mainnet — vault creation requires USDC seed instead
|
|
1249
1482
|
*/
|
|
1250
1483
|
async getRequirements() {
|
|
1251
|
-
const
|
|
1252
|
-
this.
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
address: this.addresses.vaultFactory,
|
|
1259
|
-
abi: VAULT_FACTORY_ABI,
|
|
1260
|
-
functionName: "eXABurnFee"
|
|
1261
|
-
})
|
|
1262
|
-
]);
|
|
1263
|
-
const isBypassed = veXARequired === BigInt(0) && burnFee === BigInt(0);
|
|
1264
|
-
return { veXARequired, burnFee, isBypassed };
|
|
1484
|
+
const veXARequired = await this.publicClient.readContract({
|
|
1485
|
+
address: this.addresses.vaultFactory,
|
|
1486
|
+
abi: VAULT_FACTORY_ABI,
|
|
1487
|
+
functionName: "minimumVeEXARequired"
|
|
1488
|
+
});
|
|
1489
|
+
const isBypassed = veXARequired === BigInt(0);
|
|
1490
|
+
return { veXARequired, isBypassed };
|
|
1265
1491
|
}
|
|
1266
1492
|
/**
|
|
1267
1493
|
* Get the agent's vault address (cached)
|
|
@@ -1285,30 +1511,15 @@ var VaultManager = class {
|
|
|
1285
1511
|
this.cachedVaultAddress = vaultAddress;
|
|
1286
1512
|
return vaultAddress;
|
|
1287
1513
|
}
|
|
1288
|
-
/**
|
|
1289
|
-
* Check if the agent should create a vault based on policy and qualification
|
|
1290
|
-
*/
|
|
1291
|
-
async shouldCreateVault() {
|
|
1292
|
-
if (this.policy === "disabled") {
|
|
1293
|
-
return { should: false, reason: "Vault creation disabled by policy" };
|
|
1294
|
-
}
|
|
1295
|
-
if (this.policy === "manual") {
|
|
1296
|
-
return { should: false, reason: "Vault creation set to manual - waiting for owner instruction" };
|
|
1297
|
-
}
|
|
1298
|
-
const status = await this.getVaultStatus();
|
|
1299
|
-
if (status.hasVault) {
|
|
1300
|
-
return { should: false, reason: "Vault already exists" };
|
|
1301
|
-
}
|
|
1302
|
-
if (!status.canCreateVault) {
|
|
1303
|
-
return { should: false, reason: status.cannotCreateReason || "Requirements not met" };
|
|
1304
|
-
}
|
|
1305
|
-
return { should: true, reason: "Agent is qualified and auto-creation is enabled" };
|
|
1306
|
-
}
|
|
1307
1514
|
/**
|
|
1308
1515
|
* Create a vault for the agent
|
|
1516
|
+
* @param seedAmount - USDC seed amount in raw units (default: 100e6 = 100 USDC)
|
|
1309
1517
|
* @returns Vault address if successful
|
|
1310
1518
|
*/
|
|
1311
|
-
async createVault() {
|
|
1519
|
+
async createVault(seedAmount) {
|
|
1520
|
+
if (!this.enabled) {
|
|
1521
|
+
return { success: false, error: "Vault operations disabled (contract address not set)" };
|
|
1522
|
+
}
|
|
1312
1523
|
if (this.policy === "disabled") {
|
|
1313
1524
|
return { success: false, error: "Vault creation disabled by policy" };
|
|
1314
1525
|
}
|
|
@@ -1320,6 +1531,7 @@ var VaultManager = class {
|
|
|
1320
1531
|
if (!status.canCreateVault) {
|
|
1321
1532
|
return { success: false, error: status.cannotCreateReason || "Requirements not met" };
|
|
1322
1533
|
}
|
|
1534
|
+
const seed = seedAmount || BigInt(1e8);
|
|
1323
1535
|
const vaultName = this.config.vaultConfig.defaultName || `${this.config.agentName} Trading Vault`;
|
|
1324
1536
|
const vaultSymbol = this.config.vaultConfig.defaultSymbol || `ex${this.config.agentName.replace(/[^a-zA-Z]/g, "").slice(0, 4).toUpperCase()}`;
|
|
1325
1537
|
const feeRecipient = this.config.vaultConfig.feeRecipient || this.account.address;
|
|
@@ -1331,6 +1543,7 @@ var VaultManager = class {
|
|
|
1331
1543
|
args: [
|
|
1332
1544
|
this.config.agentId,
|
|
1333
1545
|
this.addresses.usdc,
|
|
1546
|
+
seed,
|
|
1334
1547
|
vaultName,
|
|
1335
1548
|
vaultSymbol,
|
|
1336
1549
|
feeRecipient
|
|
@@ -1394,37 +1607,12 @@ var VaultManager = class {
|
|
|
1394
1607
|
};
|
|
1395
1608
|
}
|
|
1396
1609
|
}
|
|
1397
|
-
/**
|
|
1398
|
-
* Run the auto-creation check (call this periodically in the agent loop)
|
|
1399
|
-
* Only creates vault if policy is 'auto_when_qualified'
|
|
1400
|
-
*/
|
|
1401
|
-
async checkAndAutoCreateVault() {
|
|
1402
|
-
const shouldCreate = await this.shouldCreateVault();
|
|
1403
|
-
if (!shouldCreate.should) {
|
|
1404
|
-
const status = await this.getVaultStatus();
|
|
1405
|
-
if (status.hasVault) {
|
|
1406
|
-
return { action: "already_exists", vaultAddress: status.vaultAddress, reason: "Vault already exists" };
|
|
1407
|
-
}
|
|
1408
|
-
if (this.policy !== "auto_when_qualified") {
|
|
1409
|
-
return { action: "skipped", reason: shouldCreate.reason };
|
|
1410
|
-
}
|
|
1411
|
-
return { action: "not_qualified", reason: shouldCreate.reason };
|
|
1412
|
-
}
|
|
1413
|
-
const result = await this.createVault();
|
|
1414
|
-
if (result.success) {
|
|
1415
|
-
return {
|
|
1416
|
-
action: "created",
|
|
1417
|
-
vaultAddress: result.vaultAddress,
|
|
1418
|
-
reason: "Vault created automatically"
|
|
1419
|
-
};
|
|
1420
|
-
}
|
|
1421
|
-
return { action: "not_qualified", reason: result.error || "Creation failed" };
|
|
1422
|
-
}
|
|
1423
1610
|
};
|
|
1424
1611
|
|
|
1425
1612
|
// src/relay.ts
|
|
1426
1613
|
var import_ws = __toESM(require("ws"));
|
|
1427
1614
|
var import_accounts2 = require("viem/accounts");
|
|
1615
|
+
var import_sdk = require("@exagent/sdk");
|
|
1428
1616
|
var RelayClient = class {
|
|
1429
1617
|
config;
|
|
1430
1618
|
ws = null;
|
|
@@ -1527,7 +1715,8 @@ var RelayClient = class {
|
|
|
1527
1715
|
agentId: this.config.agentId,
|
|
1528
1716
|
wallet: account.address,
|
|
1529
1717
|
timestamp,
|
|
1530
|
-
signature
|
|
1718
|
+
signature,
|
|
1719
|
+
sdkVersion: import_sdk.SDK_VERSION
|
|
1531
1720
|
});
|
|
1532
1721
|
}
|
|
1533
1722
|
/**
|
|
@@ -1689,6 +1878,7 @@ function openBrowser(url) {
|
|
|
1689
1878
|
|
|
1690
1879
|
// src/runtime.ts
|
|
1691
1880
|
var FUNDS_LOW_THRESHOLD = 5e-3;
|
|
1881
|
+
var FUNDS_CRITICAL_THRESHOLD = 1e-3;
|
|
1692
1882
|
var AgentRuntime = class {
|
|
1693
1883
|
config;
|
|
1694
1884
|
client;
|
|
@@ -1702,14 +1892,13 @@ var AgentRuntime = class {
|
|
|
1702
1892
|
isRunning = false;
|
|
1703
1893
|
mode = "idle";
|
|
1704
1894
|
configHash;
|
|
1705
|
-
lastVaultCheck = 0;
|
|
1706
1895
|
cycleCount = 0;
|
|
1707
1896
|
lastCycleAt = 0;
|
|
1708
1897
|
lastPortfolioValue = 0;
|
|
1709
1898
|
lastEthBalance = "0";
|
|
1710
1899
|
processAlive = true;
|
|
1711
|
-
|
|
1712
|
-
|
|
1900
|
+
riskUniverse = 0;
|
|
1901
|
+
allowedTokens = /* @__PURE__ */ new Set();
|
|
1713
1902
|
constructor(config) {
|
|
1714
1903
|
this.config = config;
|
|
1715
1904
|
}
|
|
@@ -1718,7 +1907,7 @@ var AgentRuntime = class {
|
|
|
1718
1907
|
*/
|
|
1719
1908
|
async initialize() {
|
|
1720
1909
|
console.log(`Initializing agent: ${this.config.name} (ID: ${this.config.agentId})`);
|
|
1721
|
-
this.client = new
|
|
1910
|
+
this.client = new import_sdk2.ExagentClient({
|
|
1722
1911
|
privateKey: this.config.privateKey,
|
|
1723
1912
|
network: this.config.network
|
|
1724
1913
|
});
|
|
@@ -1729,6 +1918,7 @@ var AgentRuntime = class {
|
|
|
1729
1918
|
}
|
|
1730
1919
|
console.log(`Agent verified: ${agent.name}`);
|
|
1731
1920
|
await this.ensureWalletLinked();
|
|
1921
|
+
await this.loadTradingRestrictions();
|
|
1732
1922
|
console.log(`Initializing LLM: ${this.config.llm.provider}`);
|
|
1733
1923
|
this.llm = await createLLMAdapter(this.config.llm);
|
|
1734
1924
|
const llmMeta = this.llm.getMetadata();
|
|
@@ -1796,12 +1986,8 @@ var AgentRuntime = class {
|
|
|
1796
1986
|
console.log(`Vault TVL: ${Number(status.totalAssets) / 1e6} USDC`);
|
|
1797
1987
|
} else {
|
|
1798
1988
|
console.log("No vault exists for this agent");
|
|
1799
|
-
if (vaultConfig.policy === "
|
|
1800
|
-
|
|
1801
|
-
console.log("Agent is qualified to create vault - will attempt on next check");
|
|
1802
|
-
} else {
|
|
1803
|
-
console.log(`Cannot create vault yet: ${status.cannotCreateReason}`);
|
|
1804
|
-
}
|
|
1989
|
+
if (vaultConfig.policy === "manual") {
|
|
1990
|
+
console.log("Vault creation is manual \u2014 use the command center to create one");
|
|
1805
1991
|
}
|
|
1806
1992
|
}
|
|
1807
1993
|
}
|
|
@@ -1820,7 +2006,7 @@ var AgentRuntime = class {
|
|
|
1820
2006
|
if (agent?.owner.toLowerCase() !== address.toLowerCase()) {
|
|
1821
2007
|
const ccUrl = `https://exagent.io/agents/${encodeURIComponent(this.config.name)}/command-center`;
|
|
1822
2008
|
const nonce = await this.client.registry.getNonce(address);
|
|
1823
|
-
const linkMessage =
|
|
2009
|
+
const linkMessage = import_sdk2.ExagentRegistry.generateLinkMessage(
|
|
1824
2010
|
address,
|
|
1825
2011
|
agentId,
|
|
1826
2012
|
nonce
|
|
@@ -1860,6 +2046,49 @@ var AgentRuntime = class {
|
|
|
1860
2046
|
console.log("Wallet already linked");
|
|
1861
2047
|
}
|
|
1862
2048
|
}
|
|
2049
|
+
/**
|
|
2050
|
+
* Load risk universe and allowed tokens from on-chain registry.
|
|
2051
|
+
* This prevents the agent from wasting gas on trades that will revert.
|
|
2052
|
+
*/
|
|
2053
|
+
async loadTradingRestrictions() {
|
|
2054
|
+
const agentId = BigInt(this.config.agentId);
|
|
2055
|
+
const RISK_UNIVERSE_NAMES = ["Core", "Established", "Derivatives", "Emerging", "Frontier"];
|
|
2056
|
+
try {
|
|
2057
|
+
this.riskUniverse = await this.client.registry.getRiskUniverse(agentId);
|
|
2058
|
+
console.log(`Risk universe: ${RISK_UNIVERSE_NAMES[this.riskUniverse] || this.riskUniverse}`);
|
|
2059
|
+
const configTokens = this.config.allowedTokens || this.getDefaultTokens();
|
|
2060
|
+
const verified = [];
|
|
2061
|
+
for (const token of configTokens) {
|
|
2062
|
+
try {
|
|
2063
|
+
const allowed = await this.client.registry.isTradeAllowed(
|
|
2064
|
+
agentId,
|
|
2065
|
+
token,
|
|
2066
|
+
"0x0000000000000000000000000000000000000000"
|
|
2067
|
+
// zero = check token only
|
|
2068
|
+
);
|
|
2069
|
+
if (allowed) {
|
|
2070
|
+
this.allowedTokens.add(token.toLowerCase());
|
|
2071
|
+
verified.push(token);
|
|
2072
|
+
} else {
|
|
2073
|
+
console.warn(`Token ${token} not allowed for this agent's risk universe \u2014 excluded`);
|
|
2074
|
+
}
|
|
2075
|
+
} catch {
|
|
2076
|
+
this.allowedTokens.add(token.toLowerCase());
|
|
2077
|
+
verified.push(token);
|
|
2078
|
+
}
|
|
2079
|
+
}
|
|
2080
|
+
this.config.allowedTokens = verified;
|
|
2081
|
+
console.log(`Allowed tokens loaded: ${verified.length} tokens verified`);
|
|
2082
|
+
if (this.riskUniverse === 4) {
|
|
2083
|
+
console.warn("Frontier risk universe: vault creation is disabled");
|
|
2084
|
+
}
|
|
2085
|
+
} catch (error) {
|
|
2086
|
+
console.warn(
|
|
2087
|
+
"Could not load trading restrictions from registry (using defaults):",
|
|
2088
|
+
error instanceof Error ? error.message : error
|
|
2089
|
+
);
|
|
2090
|
+
}
|
|
2091
|
+
}
|
|
1863
2092
|
/**
|
|
1864
2093
|
* Sync the LLM config hash to chain for epoch tracking.
|
|
1865
2094
|
* If the wallet has insufficient gas, enters a recovery loop
|
|
@@ -1868,7 +2097,7 @@ var AgentRuntime = class {
|
|
|
1868
2097
|
async syncConfigHash() {
|
|
1869
2098
|
const agentId = BigInt(this.config.agentId);
|
|
1870
2099
|
const llmMeta = this.llm.getMetadata();
|
|
1871
|
-
this.configHash =
|
|
2100
|
+
this.configHash = import_sdk2.ExagentRegistry.calculateConfigHash(llmMeta.provider, llmMeta.model);
|
|
1872
2101
|
console.log(`Config hash: ${this.configHash}`);
|
|
1873
2102
|
const onChainHash = await this.client.registry.getConfigHash(agentId);
|
|
1874
2103
|
if (onChainHash !== this.configHash) {
|
|
@@ -1881,7 +2110,7 @@ var AgentRuntime = class {
|
|
|
1881
2110
|
const message = error instanceof Error ? error.message : String(error);
|
|
1882
2111
|
if (message.includes("insufficient funds") || message.includes("gas") || message.includes("intrinsic gas too low") || message.includes("exceeds the balance")) {
|
|
1883
2112
|
const ccUrl = `https://exagent.io/agents/${encodeURIComponent(this.config.name)}/command-center`;
|
|
1884
|
-
const chain =
|
|
2113
|
+
const chain = import_chains2.base;
|
|
1885
2114
|
const publicClientInstance = (0, import_viem3.createPublicClient)({
|
|
1886
2115
|
chain,
|
|
1887
2116
|
transport: (0, import_viem3.http)(this.getRpcUrl())
|
|
@@ -2032,21 +2261,38 @@ var AgentRuntime = class {
|
|
|
2032
2261
|
break;
|
|
2033
2262
|
case "update_risk_params": {
|
|
2034
2263
|
const params = cmd.params || {};
|
|
2264
|
+
let updated = false;
|
|
2035
2265
|
if (params.maxPositionSizeBps !== void 0) {
|
|
2036
|
-
|
|
2266
|
+
const value = Number(params.maxPositionSizeBps);
|
|
2267
|
+
if (isNaN(value) || value < 100 || value > 1e4) {
|
|
2268
|
+
this.relay?.sendCommandResult(cmd.id, false, "maxPositionSizeBps must be 100-10000");
|
|
2269
|
+
break;
|
|
2270
|
+
}
|
|
2271
|
+
this.config.trading.maxPositionSizeBps = value;
|
|
2272
|
+
updated = true;
|
|
2037
2273
|
}
|
|
2038
2274
|
if (params.maxDailyLossBps !== void 0) {
|
|
2039
|
-
|
|
2275
|
+
const value = Number(params.maxDailyLossBps);
|
|
2276
|
+
if (isNaN(value) || value < 50 || value > 5e3) {
|
|
2277
|
+
this.relay?.sendCommandResult(cmd.id, false, "maxDailyLossBps must be 50-5000");
|
|
2278
|
+
break;
|
|
2279
|
+
}
|
|
2280
|
+
this.config.trading.maxDailyLossBps = value;
|
|
2281
|
+
updated = true;
|
|
2282
|
+
}
|
|
2283
|
+
if (updated) {
|
|
2284
|
+
this.riskManager = new RiskManager(this.config.trading);
|
|
2285
|
+
console.log("Risk params updated via command center");
|
|
2286
|
+
this.relay?.sendCommandResult(cmd.id, true, "Risk parameters updated");
|
|
2287
|
+
this.relay?.sendMessage(
|
|
2288
|
+
"config_updated",
|
|
2289
|
+
"info",
|
|
2290
|
+
"Risk Parameters Updated",
|
|
2291
|
+
`Max position: ${this.config.trading.maxPositionSizeBps / 100}%, Max daily loss: ${this.config.trading.maxDailyLossBps / 100}%`
|
|
2292
|
+
);
|
|
2293
|
+
} else {
|
|
2294
|
+
this.relay?.sendCommandResult(cmd.id, false, "No valid parameters provided");
|
|
2040
2295
|
}
|
|
2041
|
-
this.riskManager = new RiskManager(this.config.trading);
|
|
2042
|
-
console.log("Risk params updated via command center");
|
|
2043
|
-
this.relay?.sendCommandResult(cmd.id, true, "Risk params updated");
|
|
2044
|
-
this.relay?.sendMessage(
|
|
2045
|
-
"config_updated",
|
|
2046
|
-
"info",
|
|
2047
|
-
"Risk Parameters Updated",
|
|
2048
|
-
`Max position: ${this.config.trading.maxPositionSizeBps / 100}%, Max daily loss: ${this.config.trading.maxDailyLossBps / 100}%`
|
|
2049
|
-
);
|
|
2050
2296
|
break;
|
|
2051
2297
|
}
|
|
2052
2298
|
case "update_trading_interval": {
|
|
@@ -2123,7 +2369,7 @@ var AgentRuntime = class {
|
|
|
2123
2369
|
provider: this.config.llm.provider,
|
|
2124
2370
|
model: this.config.llm.model || "default"
|
|
2125
2371
|
},
|
|
2126
|
-
risk: this.riskManager?.getStatus() || {
|
|
2372
|
+
risk: this.riskManager?.getStatus(this.lastPortfolioValue) || {
|
|
2127
2373
|
dailyPnL: 0,
|
|
2128
2374
|
dailyLossLimit: 0,
|
|
2129
2375
|
isLimitHit: false
|
|
@@ -2144,14 +2390,18 @@ var AgentRuntime = class {
|
|
|
2144
2390
|
--- Trading Cycle: ${(/* @__PURE__ */ new Date()).toISOString()} ---`);
|
|
2145
2391
|
this.cycleCount++;
|
|
2146
2392
|
this.lastCycleAt = Date.now();
|
|
2147
|
-
await this.checkVaultAutoCreation();
|
|
2148
2393
|
const tokens = this.config.allowedTokens || this.getDefaultTokens();
|
|
2149
2394
|
const marketData = await this.marketData.fetchMarketData(this.client.address, tokens);
|
|
2150
2395
|
console.log(`Portfolio value: $${marketData.portfolioValue.toFixed(2)}`);
|
|
2151
2396
|
this.lastPortfolioValue = marketData.portfolioValue;
|
|
2152
2397
|
const nativeEthBal = marketData.balances[NATIVE_ETH.toLowerCase()] || BigInt(0);
|
|
2153
2398
|
this.lastEthBalance = (Number(nativeEthBal) / 1e18).toFixed(6);
|
|
2154
|
-
this.checkFundsLow(marketData);
|
|
2399
|
+
const fundsOk = this.checkFundsLow(marketData);
|
|
2400
|
+
if (!fundsOk) {
|
|
2401
|
+
console.warn("Skipping trading cycle \u2014 ETH balance critically low");
|
|
2402
|
+
this.sendRelayStatus();
|
|
2403
|
+
return;
|
|
2404
|
+
}
|
|
2155
2405
|
let signals;
|
|
2156
2406
|
try {
|
|
2157
2407
|
signals = await this.strategy(marketData, this.llm, this.config);
|
|
@@ -2169,19 +2419,31 @@ var AgentRuntime = class {
|
|
|
2169
2419
|
console.log(`Strategy generated ${signals.length} signals`);
|
|
2170
2420
|
const filteredSignals = this.riskManager.filterSignals(signals, marketData);
|
|
2171
2421
|
console.log(`${filteredSignals.length} signals passed risk checks`);
|
|
2172
|
-
if (this.riskManager.getStatus().isLimitHit) {
|
|
2422
|
+
if (this.riskManager.getStatus(marketData.portfolioValue).isLimitHit) {
|
|
2173
2423
|
this.relay?.sendMessage(
|
|
2174
2424
|
"risk_limit_hit",
|
|
2175
2425
|
"warning",
|
|
2176
2426
|
"Risk Limit Hit",
|
|
2177
|
-
`Daily loss limit reached:
|
|
2427
|
+
`Daily loss limit reached: $${this.riskManager.getStatus(marketData.portfolioValue).dailyPnL.toFixed(2)}`
|
|
2178
2428
|
);
|
|
2179
2429
|
}
|
|
2180
2430
|
if (filteredSignals.length > 0) {
|
|
2431
|
+
const vaultStatus = await this.vaultManager?.getVaultStatus();
|
|
2432
|
+
if (vaultStatus?.hasVault && this.vaultManager?.preferVaultTrading) {
|
|
2433
|
+
console.log(`Trading through vault: ${vaultStatus.vaultAddress}`);
|
|
2434
|
+
}
|
|
2435
|
+
const preTradePortfolioValue = marketData.portfolioValue;
|
|
2181
2436
|
const results = await this.executor.executeAll(filteredSignals);
|
|
2437
|
+
let totalFeesUSD = 0;
|
|
2182
2438
|
for (const result of results) {
|
|
2183
2439
|
if (result.success) {
|
|
2184
2440
|
console.log(`Trade executed: ${result.signal.action} - ${result.txHash}`);
|
|
2441
|
+
const feeCostBps = 20;
|
|
2442
|
+
const signalPrice = marketData.prices[result.signal.tokenIn.toLowerCase()] || 0;
|
|
2443
|
+
const amountUSD = Number(result.signal.amountIn) / Math.pow(10, getTokenDecimals(result.signal.tokenIn)) * signalPrice;
|
|
2444
|
+
const feeCostUSD = amountUSD * feeCostBps / 1e4;
|
|
2445
|
+
totalFeesUSD += feeCostUSD;
|
|
2446
|
+
this.riskManager.updateFees(feeCostUSD);
|
|
2185
2447
|
this.relay?.sendMessage(
|
|
2186
2448
|
"trade_executed",
|
|
2187
2449
|
"success",
|
|
@@ -2205,18 +2467,43 @@ var AgentRuntime = class {
|
|
|
2205
2467
|
);
|
|
2206
2468
|
}
|
|
2207
2469
|
}
|
|
2470
|
+
const postTokens = this.config.allowedTokens || this.getDefaultTokens();
|
|
2471
|
+
const postTradeData = await this.marketData.fetchMarketData(this.client.address, postTokens);
|
|
2472
|
+
const marketPnL = postTradeData.portfolioValue - preTradePortfolioValue + totalFeesUSD;
|
|
2473
|
+
this.riskManager.updatePnL(marketPnL);
|
|
2474
|
+
if (marketPnL !== 0) {
|
|
2475
|
+
console.log(`Cycle PnL: $${marketPnL.toFixed(2)} (market), -$${totalFeesUSD.toFixed(2)} (fees)`);
|
|
2476
|
+
}
|
|
2477
|
+
this.lastPortfolioValue = postTradeData.portfolioValue;
|
|
2478
|
+
const postNativeEthBal = postTradeData.balances[NATIVE_ETH.toLowerCase()] || BigInt(0);
|
|
2479
|
+
this.lastEthBalance = (Number(postNativeEthBal) / 1e18).toFixed(6);
|
|
2208
2480
|
}
|
|
2209
2481
|
this.sendRelayStatus();
|
|
2210
2482
|
}
|
|
2211
2483
|
/**
|
|
2212
|
-
* Check if ETH balance is below threshold and notify
|
|
2484
|
+
* Check if ETH balance is below threshold and notify.
|
|
2485
|
+
* Returns true if trading should continue, false if ETH is critically low.
|
|
2213
2486
|
*/
|
|
2214
2487
|
checkFundsLow(marketData) {
|
|
2215
|
-
if (!this.relay) return;
|
|
2216
2488
|
const ethBalance = marketData.balances[NATIVE_ETH.toLowerCase()] || BigInt(0);
|
|
2217
2489
|
const ethAmount = Number(ethBalance) / 1e18;
|
|
2490
|
+
if (ethAmount < FUNDS_CRITICAL_THRESHOLD) {
|
|
2491
|
+
console.error(`ETH balance critically low: ${ethAmount.toFixed(6)} ETH \u2014 halting trading`);
|
|
2492
|
+
this.relay?.sendMessage(
|
|
2493
|
+
"funds_low",
|
|
2494
|
+
"error",
|
|
2495
|
+
"Funds Critical",
|
|
2496
|
+
`ETH balance is ${ethAmount.toFixed(6)} ETH (below ${FUNDS_CRITICAL_THRESHOLD} ETH minimum). Trading halted \u2014 fund your wallet.`,
|
|
2497
|
+
{
|
|
2498
|
+
ethBalance: ethAmount.toFixed(6),
|
|
2499
|
+
wallet: this.client.address,
|
|
2500
|
+
threshold: FUNDS_CRITICAL_THRESHOLD
|
|
2501
|
+
}
|
|
2502
|
+
);
|
|
2503
|
+
return false;
|
|
2504
|
+
}
|
|
2218
2505
|
if (ethAmount < FUNDS_LOW_THRESHOLD) {
|
|
2219
|
-
this.relay
|
|
2506
|
+
this.relay?.sendMessage(
|
|
2220
2507
|
"funds_low",
|
|
2221
2508
|
"warning",
|
|
2222
2509
|
"Low Funds",
|
|
@@ -2228,36 +2515,7 @@ var AgentRuntime = class {
|
|
|
2228
2515
|
}
|
|
2229
2516
|
);
|
|
2230
2517
|
}
|
|
2231
|
-
|
|
2232
|
-
/**
|
|
2233
|
-
* Check for vault auto-creation based on policy
|
|
2234
|
-
*/
|
|
2235
|
-
async checkVaultAutoCreation() {
|
|
2236
|
-
const now = Date.now();
|
|
2237
|
-
if (now - this.lastVaultCheck < this.VAULT_CHECK_INTERVAL) {
|
|
2238
|
-
return;
|
|
2239
|
-
}
|
|
2240
|
-
this.lastVaultCheck = now;
|
|
2241
|
-
const result = await this.vaultManager.checkAndAutoCreateVault();
|
|
2242
|
-
switch (result.action) {
|
|
2243
|
-
case "created":
|
|
2244
|
-
console.log(`Vault created automatically: ${result.vaultAddress}`);
|
|
2245
|
-
this.relay?.sendMessage(
|
|
2246
|
-
"vault_created",
|
|
2247
|
-
"success",
|
|
2248
|
-
"Vault Auto-Created",
|
|
2249
|
-
`Vault deployed at ${result.vaultAddress}`,
|
|
2250
|
-
{ vaultAddress: result.vaultAddress }
|
|
2251
|
-
);
|
|
2252
|
-
break;
|
|
2253
|
-
case "already_exists":
|
|
2254
|
-
break;
|
|
2255
|
-
case "skipped":
|
|
2256
|
-
break;
|
|
2257
|
-
case "not_qualified":
|
|
2258
|
-
console.log(`Vault auto-creation pending: ${result.reason}`);
|
|
2259
|
-
break;
|
|
2260
|
-
}
|
|
2518
|
+
return true;
|
|
2261
2519
|
}
|
|
2262
2520
|
/**
|
|
2263
2521
|
* Stop the agent process completely
|
|
@@ -2275,26 +2533,44 @@ var AgentRuntime = class {
|
|
|
2275
2533
|
* Get RPC URL based on network
|
|
2276
2534
|
*/
|
|
2277
2535
|
getRpcUrl() {
|
|
2278
|
-
|
|
2279
|
-
return "https://mainnet.base.org";
|
|
2280
|
-
}
|
|
2281
|
-
return "https://sepolia.base.org";
|
|
2536
|
+
return "https://mainnet.base.org";
|
|
2282
2537
|
}
|
|
2283
2538
|
/**
|
|
2284
|
-
* Default tokens to track
|
|
2539
|
+
* Default tokens to track.
|
|
2540
|
+
* These are validated against the on-chain registry's isTradeAllowed() during init —
|
|
2541
|
+
* agents in restricted risk universes will have ineligible tokens filtered out.
|
|
2285
2542
|
*/
|
|
2286
2543
|
getDefaultTokens() {
|
|
2287
|
-
if (this.config.network === "mainnet") {
|
|
2288
|
-
return [
|
|
2289
|
-
"0x4200000000000000000000000000000000000006",
|
|
2290
|
-
// WETH
|
|
2291
|
-
"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
|
|
2292
|
-
// USDC
|
|
2293
|
-
];
|
|
2294
|
-
}
|
|
2295
2544
|
return [
|
|
2296
|
-
|
|
2545
|
+
// Core
|
|
2546
|
+
"0x4200000000000000000000000000000000000006",
|
|
2297
2547
|
// WETH
|
|
2548
|
+
"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
2549
|
+
// USDC
|
|
2550
|
+
"0x2Ae3F1Ec7F1F5012CFEab0185bFC7aa3cf0DEC22",
|
|
2551
|
+
// cbETH
|
|
2552
|
+
"0xd9aAEc86B65D86f6A7B5B1b0c42FFA531710b6CA",
|
|
2553
|
+
// USDbC
|
|
2554
|
+
"0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb",
|
|
2555
|
+
// DAI
|
|
2556
|
+
// Established
|
|
2557
|
+
"0x940181a94A35A4569E4529A3CDfB74e38FD98631",
|
|
2558
|
+
// AERO
|
|
2559
|
+
"0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf",
|
|
2560
|
+
// cbBTC
|
|
2561
|
+
"0xc1CBa3fCea344f92D9239c08C0568f6F2F0ee452",
|
|
2562
|
+
// wstETH
|
|
2563
|
+
"0x2416092f143378750bb29b79eD961ab195CcEea5",
|
|
2564
|
+
// ezETH
|
|
2565
|
+
// Emerging (filtered by risk universe at init)
|
|
2566
|
+
"0x532f27101965dd16442E59d40670FaF5eBB142E4",
|
|
2567
|
+
// BRETT
|
|
2568
|
+
"0x4ed4E862860beD51a9570b96d89aF5E1B0Efefed",
|
|
2569
|
+
// DEGEN
|
|
2570
|
+
"0x0b3e328455c4059EEb9e3f84b5543F74E24e7E1b",
|
|
2571
|
+
// VIRTUAL
|
|
2572
|
+
"0xAC1Bd2486Aaf3B5C0fc3Fd868558b082a531B2B4"
|
|
2573
|
+
// TOSHI
|
|
2298
2574
|
];
|
|
2299
2575
|
}
|
|
2300
2576
|
sleep(ms) {
|
|
@@ -2315,7 +2591,7 @@ var AgentRuntime = class {
|
|
|
2315
2591
|
model: this.config.llm.model || "default"
|
|
2316
2592
|
},
|
|
2317
2593
|
configHash: this.configHash || "not initialized",
|
|
2318
|
-
risk: this.riskManager?.getStatus() || { dailyPnL: 0, dailyLossLimit: 0, isLimitHit: false },
|
|
2594
|
+
risk: this.riskManager?.getStatus(this.lastPortfolioValue) || { dailyPnL: 0, dailyLossLimit: 0, isLimitHit: false },
|
|
2319
2595
|
vault: {
|
|
2320
2596
|
policy: vaultConfig.policy,
|
|
2321
2597
|
hasVault: false,
|
|
@@ -2377,16 +2653,18 @@ var TradingConfigSchema = import_zod.z.object({
|
|
|
2377
2653
|
maxDailyLossBps: import_zod.z.number().min(0).max(1e4).default(500),
|
|
2378
2654
|
// 0-100%
|
|
2379
2655
|
maxConcurrentPositions: import_zod.z.number().min(1).max(100).default(5),
|
|
2380
|
-
tradingIntervalMs: import_zod.z.number().min(1e3).default(6e4)
|
|
2656
|
+
tradingIntervalMs: import_zod.z.number().min(1e3).default(6e4),
|
|
2381
2657
|
// minimum 1 second
|
|
2658
|
+
maxSlippageBps: import_zod.z.number().min(10).max(1e3).default(100),
|
|
2659
|
+
// 0.1-10%, default 1%
|
|
2660
|
+
minTradeValueUSD: import_zod.z.number().min(0).default(1)
|
|
2661
|
+
// minimum trade value in USD
|
|
2382
2662
|
});
|
|
2383
2663
|
var VaultPolicySchema = import_zod.z.enum([
|
|
2384
2664
|
"disabled",
|
|
2385
2665
|
// Never create a vault - trade with agent's own capital only
|
|
2386
|
-
"manual"
|
|
2666
|
+
"manual"
|
|
2387
2667
|
// Only create vault when explicitly directed by owner
|
|
2388
|
-
"auto_when_qualified"
|
|
2389
|
-
// Automatically create vault when requirements are met
|
|
2390
2668
|
]);
|
|
2391
2669
|
var VaultConfigSchema = import_zod.z.object({
|
|
2392
2670
|
// Policy for vault creation (asked during deployment)
|
|
@@ -2414,7 +2692,7 @@ var AgentConfigSchema = import_zod.z.object({
|
|
|
2414
2692
|
agentId: import_zod.z.union([import_zod.z.number().positive(), import_zod.z.string()]),
|
|
2415
2693
|
name: import_zod.z.string().min(3).max(32),
|
|
2416
2694
|
// Network
|
|
2417
|
-
network: import_zod.z.
|
|
2695
|
+
network: import_zod.z.literal("mainnet").default("mainnet"),
|
|
2418
2696
|
// Wallet setup preference
|
|
2419
2697
|
wallet: WalletConfigSchema,
|
|
2420
2698
|
// LLM
|
|
@@ -2855,7 +3133,7 @@ async function checkFirstRunSetup(configPath) {
|
|
|
2855
3133
|
EXAGENT_PRIVATE_KEY=${privateKey}
|
|
2856
3134
|
|
|
2857
3135
|
# Network
|
|
2858
|
-
EXAGENT_NETWORK=${config.network || "
|
|
3136
|
+
EXAGENT_NETWORK=${config.network || "mainnet"}
|
|
2859
3137
|
|
|
2860
3138
|
# LLM (${llmProvider})
|
|
2861
3139
|
${llmEnvVar}EXAGENT_LLM_MODEL=${config.llm?.model || ""}
|
|
@@ -2906,7 +3184,7 @@ ${llmEnvVar}EXAGENT_LLM_MODEL=${config.llm?.model || ""}
|
|
|
2906
3184
|
console.log("");
|
|
2907
3185
|
if (walletSetup === "generate") {
|
|
2908
3186
|
console.log(" NEXT: Fund your wallet before starting to trade.");
|
|
2909
|
-
console.log(` Send
|
|
3187
|
+
console.log(` Send ETH to: ${walletAddress}`);
|
|
2910
3188
|
}
|
|
2911
3189
|
console.log("");
|
|
2912
3190
|
console.log(" The agent will now start...");
|