Loading...
Loading...
Compare original and translation side by side
+----------------------------------------------------------------+
| AUTONOMOUS AGENT FLOW |
| |
| 1. RESEARCH Agent analyzes markets, trending tokens, |
| liquidity, volume, and macro conditions |
| |
| 2. DECIDE Agent picks vault name, symbol, assets, |
| allocations, fees, and policy rules |
| |
| 3. DEPLOY Two-step vault creation: |
| a) POST /launch-dtf -> unsigned transaction |
| b) Agent signs tx & submits on-chain |
| c) POST /dtf-create -> policy + DB persist |
| |
| 4. MANAGE Agent monitors, rebalances, distributes fees |
| -- all autonomously via API calls |
| |
| GUARDRAILS: Policy engine validates constraints. |
| Invalid payloads return errors with specifics. |
| NO human confirmation step. |
+----------------------------------------------------------------+| Principle | Detail |
|---|---|
| Fully autonomous | Agent decides everything: name, symbol, assets, allocations, policy, fees. No confirmation prompts. |
| Two-step vault creation | |
| Policy engine as guardrail | Backend validates the payload and policy config. If valid, it deploys. If not, it returns an error with the specific issue. |
| Non-custodial | Agent Wallet private key never leaves the user's machine. Backend never receives secret keys. |
| Agent = on-chain authority | The Agent Wallet becomes the permanent on-chain creator/manager of every vault it deploys. |
+----------------------------------------------------------------+
| AUTONOMOUS AGENT FLOW |
| |
| 1. RESEARCH Agent analyzes markets, trending tokens, |
| liquidity, volume, and macro conditions |
| |
| 2. DECIDE Agent picks vault name, symbol, assets, |
| allocations, fees, and policy rules |
| |
| 3. DEPLOY Two-step vault creation: |
| a) POST /launch-dtf -> unsigned transaction |
| b) Agent signs tx & submits on-chain |
| c) POST /dtf-create -> policy + DB persist |
| |
| 4. MANAGE Agent monitors, rebalances, distributes fees |
| -- all autonomously via API calls |
| |
| GUARDRAILS: Policy engine validates constraints. |
| Invalid payloads return errors with specifics. |
| NO human confirmation step. |
+----------------------------------------------------------------+| 原则 | 细节 |
|---|---|
| 完全自主 | Agent决定所有事项:名称、符号、资产、配置、策略、费用。无确认提示。 |
| 两步金库创建流程 | |
| 策略引擎作为防护机制 | 后端验证负载和策略配置。若有效则部署,若无效则返回包含具体问题的错误。 |
| 非托管 | Agent Wallet私钥永远不会离开用户设备。后端永远不会接收密钥。 |
| Agent = 链上权限主体 | Agent Wallet成为其部署的每个金库的永久链上创建者/管理者。 |
DFM_AUTH_TOKENDFM_AGENT_KEYPAIRnode -e 'console.log(process.env.DFM_AUTH_TOKEN ? "set" : "not set")'undefinedDFM_AUTH_TOKENDFM_AGENT_KEYPAIRnode -e 'console.log(process.env.DFM_AUTH_TOKEN ? "set" : "not set")'undefined
**ALL API calls MUST use inline `node -e` scripts** that read env vars internally via `process.env`. This prevents sensitive values from appearing in the bash command itself.
**Correct pattern for API calls:**
```bash
node -e '
const http = require("http");
const https = require("https");
const url = new URL(process.env.DFM_API_URL + "/api/v2/agent/dtf/SYMBOL/state");
const client = url.protocol === "https:" ? https : http;
const req = client.get(url, { headers: { "Authorization": "Bearer " + process.env.DFM_AUTH_TOKEN } }, (res) => {
let data = "";
res.on("data", (chunk) => data += chunk);
res.on("end", () => console.log(data));
});
req.on("error", (e) => console.error("Error:", e.message));
'curlnode -eprocess.envtimeout: 600000run_in_background: true
**所有API调用必须使用内嵌的`node -e`脚本**,通过`process.env`内部读取环境变量。这样可防止敏感值出现在bash命令本身中。
**API调用的正确模式:**
```bash
node -e '
const http = require("http");
const https = require("https");
const url = new URL(process.env.DFM_API_URL + "/api/v2/agent/dtf/SYMBOL/state");
const client = url.protocol === "https:" ? https : http;
const req = client.get(url, { headers: { "Authorization": "Bearer " + process.env.DFM_AUTH_TOKEN } }, (res) => {
let data = "";
res.on("data", (chunk) => data += chunk);
res.on("end", () => console.log(data));
});
req.on("error", (e) => console.error("Error:", e.message));
'curlprocess.envnode -etimeout: 600000run_in_background: true.claude/settings.json.claude/settings.json~/.zshrc~/.bashrcexport.claude/settings.json~/.zshrc.claude/settings.jsonnode -e '
const fs = require("fs");
const path = require("path");
const os = require("os");
const settingsPath = path.join(process.cwd(), ".claude", "settings.json");
let settings = {};
try { settings = JSON.parse(fs.readFileSync(settingsPath, "utf8")); } catch {}
if (!settings.env) settings.env = {};
// Reject sentinel/placeholder values that should never be honoured.
const isInvalid = (v) => {
if (!v || typeof v !== "string") return true;
const t = v.trim();
if (!t) return true;
if (t === "+token+" || t === "<token>" || t.startsWith("+")) return true;
if (t.startsWith("\"") || t.endsWith("\"")) return true; // stray quotes from bad templating
return false;
};
let zshrc = "";
try { zshrc = fs.readFileSync(path.join(os.homedir(), ".zshrc"), "utf8"); } catch {}
const envVars = ["DFM_API_URL", "DFM_AUTH_TOKEN", "DFM_AGENT_KEYPAIR", "SOLANA_RPC_URL", "AGENT_WALLET_PATH"];
for (const v of envVars) {
// 1) Keep settings.json value if already valid (it is updated in-process by refresh / launch scripts).
if (!isInvalid(settings.env[v])) continue;
// 2) Pull from ~/.zshrc. Use a global regex and take the LAST match — newest export wins
// when the file accumulates multiple `export VAR=...` lines.
const re = new RegExp("export\\s+" + v + "=[\"\\047]?([^\"\\047\\n]+)[\"\\047]?", "g");
let lastMatch = null, m;
while ((m = re.exec(zshrc)) !== null) lastMatch = m;
if (lastMatch && !isInvalid(lastMatch[1])) {
settings.env[v] = lastMatch[1];
continue;
}
// 3) Final fallback: current process env
if (!isInvalid(process.env[v])) settings.env[v] = process.env[v];
else delete settings.env[v]; // ensure no garbage placeholder lingers
}
fs.mkdirSync(path.dirname(settingsPath), { recursive: true });
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
for (const v of envVars) {
console.log(v + "=" + (!isInvalid(settings.env[v]) ? "set" : "NOT SET"));
}
'DFM_AUTH_TOKENPOST {DFM_API_URL}/api/v2/agent/token/refresh-by-wallet.claude/settings.jsonexport DFM_AUTH_TOKEN=~/.zshrcSTATUS=success.claude/settings.json+token+settings.json.claude/refresh-token.jsnode .claude/refresh-token.js <WALLET_ADDRESS>const http = require("http");
const https = require("https");
const fs = require("fs");
const path = require("path");
const os = require("os");
const apiUrl = process.env.DFM_API_URL;
const walletAddress = process.argv[2];
if (!apiUrl) { console.log("ERROR: DFM_API_URL not set"); process.exit(1); }
if (!walletAddress) { console.log("ERROR: usage: node refresh-token.js <walletAddress>"); process.exit(1); }
const payload = JSON.stringify({ walletAddress });
const url = new URL(apiUrl + "/api/v2/agent/token/refresh-by-wallet");
const client = url.protocol === "https:" ? https : http;
const req = client.request(url, {
method: "POST",
headers: { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(payload) }
}, (res) => {
let data = "";
res.on("data", (c) => data += c);
res.on("end", () => {
try {
const json = JSON.parse(data);
// Backend response shape: { status, message, data: { token, expires, expiresPrettyPrint, expiresAt } }
const token = json.data?.token || json.token;
if (!token || typeof token !== "string" || token.startsWith("+")) {
console.log("ERROR: No valid token in response: " + data);
process.exit(1);
}
// (1) Update .claude/settings.json
const sp = path.join(process.cwd(), ".claude", "settings.json");
let s = {}; try { s = JSON.parse(fs.readFileSync(sp, "utf8")); } catch {}
if (!s.env) s.env = {};
s.env.DFM_AUTH_TOKEN = token;
fs.mkdirSync(path.dirname(sp), { recursive: true });
fs.writeFileSync(sp, JSON.stringify(s, null, 2));
// (2) REPLACE any existing DFM_AUTH_TOKEN export in ~/.zshrc; never append duplicates.
const zshrcPath = path.join(os.homedir(), ".zshrc");
let zshrc = "";
try { zshrc = fs.readFileSync(zshrcPath, "utf8"); } catch {}
const newLine = "export DFM_AUTH_TOKEN=\"" + token + "\"";
const lineRe = /^\s*export\s+DFM_AUTH_TOKEN=.*$/gm;
if (lineRe.test(zshrc)) {
zshrc = zshrc.replace(lineRe, newLine);
} else {
if (zshrc.length && !zshrc.endsWith("\n")) zshrc += "\n";
zshrc += newLine + "\n";
}
fs.writeFileSync(zshrcPath, zshrc);
// Output ONLY safe info — NEVER the token
console.log("STATUS=success");
console.log("DFM_AUTH_TOKEN=set");
} catch (e) {
console.log("ERROR: " + e.message);
process.exit(1);
}
});
});
req.on("error", (e) => { console.log("ERROR: " + e.message); process.exit(1); });
req.write(payload);
req.end();DFM_AUTH_TOKENTo set up your DFM Agent, I need the Solana wallet address you registered on the DFM Dashboard (https://qa.dfm.finance). Please paste your wallet public key.
DFM_AGENT_KEYPAIRAGENT_WALLET_PATH~/.dfm/agent-wallet.jsonprofile-launch.js409 "Username is already taken".claude/profile-launch.jsnode .claude/profile-launch.jsconst http = require("http");
const https = require("https");
const fs = require("fs");
const path = require("path");
const os = require("os");
const crypto = require("crypto");
const { Keypair } = require("@solana/web3.js");
const bs58 = require("bs58").default || require("bs58");
const apiUrl = process.env.DFM_API_URL;
const walletAddress = process.argv[2];
const baseName = process.argv[3];
const baseUsername = process.argv[4];
if (!apiUrl) { console.log("ERROR: DFM_API_URL not set"); process.exit(1); }
if (!walletAddress || !baseName || !baseUsername) {
console.log("ERROR: usage: node profile-launch.js <walletAddress> <name> <username>");
process.exit(1);
}
// Derive agent wallet public key from DFM_AGENT_KEYPAIR
const agentKeypair = Keypair.fromSecretKey(bs58.decode(process.env.DFM_AGENT_KEYPAIR));
const agentWalletAddress = agentKeypair.publicKey.toBase58();
const MAX_ATTEMPTS = 5;
const UNAME_RX = /username/i; // disambiguates duplicate-username vs. wallet-already-has-agent
// Sanitize base username to allowed charset (alphanumeric + underscore)
const sanitize = (s) => s.toLowerCase().replace(/[^a-z0-9_]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "");
const cleanBase = sanitize(baseUsername) || "agent";
// Suffix generator: deterministic-looking but unique. 4 hex chars = 65k combos.
const suffix = () => crypto.randomBytes(2).toString("hex");
function attempt(usernameToTry, agentNameToTry, n) {
const payload = JSON.stringify({
userPublicKey: walletAddress,
agentWalletAddress: agentWalletAddress,
name: agentNameToTry,
username: usernameToTry,
metadata: [{ key: "created_by", value: "dfm-agent-skill" }]
});
const url = new URL(apiUrl + "/api/v2/agent/profile-launch");
const client = url.protocol === "https:" ? https : http;
const req = client.request(url, {
method: "POST",
headers: { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(payload) }
}, (res) => {
let data = "";
res.on("data", (chunk) => data += chunk);
res.on("end", () => {
let json = null;
try { json = JSON.parse(data); } catch { /* fall through */ }
const message = json?.message || json?.error || data;
// 409 + duplicate-username -> regenerate username, retry. Do NOT retry on
// "agent profile already exists for this wallet" — that is unrecoverable here.
if (res.statusCode === 409 && UNAME_RX.test(String(message)) && !/wallet/i.test(String(message))) {
if (n >= MAX_ATTEMPTS) {
console.log("ERROR: username conflicts after " + MAX_ATTEMPTS + " attempts. Last tried: " + usernameToTry);
process.exit(1);
}
const nextUsername = cleanBase + "_" + suffix();
const nextName = baseName; // keep display name; only username needs to be unique
console.log("RETRY=username_taken attempt=" + (n + 1) + " next_username=" + nextUsername);
return attempt(nextUsername, nextName, n + 1);
}
if (res.statusCode === 409) {
console.log("ERROR: 409 Conflict (not username-related): " + message);
process.exit(1);
}
if (res.statusCode < 200 || res.statusCode >= 300) {
console.log("ERROR: HTTP " + res.statusCode + ": " + message);
process.exit(1);
}
const token = json?.data?.token?.token;
if (!token) { console.log("ERROR: No token in response. Response: " + data); process.exit(1); }
// Write to .claude/settings.json
const sp = path.join(process.cwd(), ".claude", "settings.json");
let s = {}; try { s = JSON.parse(fs.readFileSync(sp, "utf8")); } catch {}
if (!s.env) s.env = {};
s.env.DFM_AUTH_TOKEN = token;
fs.writeFileSync(sp, JSON.stringify(s, null, 2));
// REPLACE any existing DFM_AUTH_TOKEN export in ~/.zshrc; never append duplicates.
const zshrcPath = path.join(os.homedir(), ".zshrc");
let zshrc = "";
try { zshrc = fs.readFileSync(zshrcPath, "utf8"); } catch {}
const newLine = "export DFM_AUTH_TOKEN=\"" + token + "\"";
const lineRe = /^\s*export\s+DFM_AUTH_TOKEN=.*$/gm;
if (lineRe.test(zshrc)) {
zshrc = zshrc.replace(lineRe, newLine);
} else {
if (zshrc.length && !zshrc.endsWith("\n")) zshrc += "\n";
zshrc += newLine + "\n";
}
fs.writeFileSync(zshrcPath, zshrc);
// Only output safe info — NEVER the token
const profileName = json.data?.agentProfile?.name || agentNameToTry;
const profileUsername = json.data?.agentProfile?.username || usernameToTry;
console.log("STATUS=success");
console.log("AGENT_NAME=" + profileName);
console.log("AGENT_USERNAME=" + profileUsername);
console.log("AGENT_WALLET=" + agentWalletAddress);
console.log("ATTEMPTS=" + n);
console.log("DFM_AUTH_TOKEN=set");
});
});
req.on("error", (e) => { console.log("ERROR: " + e.message); process.exit(1); });
req.write(payload);
req.end();
}
attempt(cleanBase, baseName, 1);node .claude/profile-launch.js <WALLET_ADDRESS> "<AGENT_NAME>" "<AGENT_USERNAME>"agentWalletAddressDFM_AGENT_KEYPAIRSTATUS=successAGENT_NAME=...AGENT_USERNAME=...AGENT_WALLET=...ATTEMPTS=<n>DFM_AUTH_TOKEN=set.claude/settings.json~/.zshrc409"Username is already taken"POST /profile-launchRETRY=username_taken attempt=<n> next_username=<new>nameusername409"An agent profile already exists for this wallet address"/token/refresh-by-walletERROR:DFM_AGENT_KEYPAIR~/.zshrc~/.bashrcexport.claude/settings.json~/.zshrc.claude/settings.jsonnode -e '
const fs = require("fs");
const path = require("path");
const os = require("os");
const settingsPath = path.join(process.cwd(), ".claude", "settings.json");
let settings = {};
try { settings = JSON.parse(fs.readFileSync(settingsPath, "utf8")); } catch {}
if (!settings.env) settings.env = {};
// 拒绝不应被使用的标记/占位值。
const isInvalid = (v) => {
if (!v || typeof v !== "string") return true;
const t = v.trim();
if (!t) return true;
if (t === "+token+" || t === "<token>" || t.startsWith("+")) return true;
if (t.startsWith("\"") || t.endsWith("\"")) return true; // 错误模板导致的多余引号
return false;
};
let zshrc = "";
try { zshrc = fs.readFileSync(path.join(os.homedir(), ".zshrc"), "utf8"); } catch {}
const envVars = ["DFM_API_URL", "DFM_AUTH_TOKEN", "DFM_AGENT_KEYPAIR", "SOLANA_RPC_URL", "AGENT_WALLET_PATH"];
for (const v of envVars) {
// 1) 如果settings.json中的值已有效则保留(它会被刷新/启动脚本在进程中更新)。
if (!isInvalid(settings.env[v])) continue;
// 2) 从~/.zshrc中提取。使用全局正则表达式并取最后一个匹配项——当文件积累多个`export VAR=...`行时,最新的导出生效。
const re = new RegExp("export\\s+" + v + "=[\"\\047]?([^\"\\047\\n]+)[\"\\047]?", "g");
let lastMatch = null, m;
while ((m = re.exec(zshrc)) !== null) lastMatch = m;
if (lastMatch && !isInvalid(lastMatch[1])) {
settings.env[v] = lastMatch[1];
continue;
}
// 3) 最终回退:当前进程环境
if (!isInvalid(process.env[v])) settings.env[v] = process.env[v];
else delete settings.env[v]; // 确保没有无效占位符残留
}
fs.mkdirSync(path.dirname(settingsPath), { recursive: true });
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
for (const v of envVars) {
console.log(v + "=" + (!isInvalid(settings.env[v]) ? "set" : "NOT SET"));
}
'DFM_AUTH_TOKENPOST {DFM_API_URL}/api/v2/agent/token/refresh-by-wallet.claude/settings.json~/.zshrcexport DFM_AUTH_TOKEN=STATUS=success.claude/settings.json+token+settings.json.claude/refresh-token.jsnode .claude/refresh-token.js <WALLET_ADDRESS>const http = require("http");
const https = require("https");
const fs = require("fs");
const path = require("path");
const os = require("os");
const apiUrl = process.env.DFM_API_URL;
const walletAddress = process.argv[2];
if (!apiUrl) { console.log("ERROR: DFM_API_URL not set"); process.exit(1); }
if (!walletAddress) { console.log("ERROR: usage: node refresh-token.js <walletAddress>"); process.exit(1); }
const payload = JSON.stringify({ walletAddress });
const url = new URL(apiUrl + "/api/v2/agent/token/refresh-by-wallet");
const client = url.protocol === "https:" ? https : http;
const req = client.request(url, {
method: "POST",
headers: { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(payload) }
}, (res) => {
let data = "";
res.on("data", (c) => data += c);
res.on("end", () => {
try {
const json = JSON.parse(data);
// 后端响应格式:{ status, message, data: { token, expires, expiresPrettyPrint, expiresAt } }
const token = json.data?.token || json.token;
if (!token || typeof token !== "string" || token.startsWith("+")) {
console.log("ERROR: No valid token in response: " + data);
process.exit(1);
}
// (1) 更新.claude/settings.json
const sp = path.join(process.cwd(), ".claude", "settings.json");
let s = {}; try { s = JSON.parse(fs.readFileSync(sp, "utf8")); } catch {}
if (!s.env) s.env = {};
s.env.DFM_AUTH_TOKEN = token;
fs.mkdirSync(path.dirname(sp), { recursive: true });
fs.writeFileSync(sp, JSON.stringify(s, null, 2));
// (2) 替换~/.zshrc中任何现有的DFM_AUTH_TOKEN导出;绝不追加重复项。
const zshrcPath = path.join(os.homedir(), ".zshrc");
let zshrc = "";
try { zshrc = fs.readFileSync(zshrcPath, "utf8"); } catch {}
const newLine = "export DFM_AUTH_TOKEN=\"" + token + "\"";
const lineRe = /^\s*export\s+DFM_AUTH_TOKEN=.*$/gm;
if (lineRe.test(zshrc)) {
zshrc = zshrc.replace(lineRe, newLine);
} else {
if (zshrc.length && !zshrc.endsWith("\n")) zshrc += "\n";
zshrc += newLine + "\n";
}
fs.writeFileSync(zshrcPath, zshrc);
// 仅输出安全信息——绝不输出令牌
console.log("STATUS=success");
console.log("DFM_AUTH_TOKEN=set");
} catch (e) {
console.log("ERROR: " + e.message);
process.exit(1);
}
});
});
req.on("error", (e) => { console.log("ERROR: " + e.message); process.exit(1); });
req.write(payload);
req.end();DFM_AUTH_TOKEN要设置您的DFM Agent,我需要您在DFM仪表板(https://qa.dfm.finance)上注册的**Solana钱包地址**。 请粘贴您的钱包公钥。
DFM_AGENT_KEYPAIRAGENT_WALLET_PATH~/.dfm/agent-wallet.json409 "Username is already taken"profile-launch.js.claude/profile-launch.jsnode .claude/profile-launch.jsconst http = require("http");
const https = require("https");
const fs = require("fs");
const path = require("path");
const os = require("os");
const crypto = require("crypto");
const { Keypair } = require("@solana/web3.js");
const bs58 = require("bs58").default || require("bs58");
const apiUrl = process.env.DFM_API_URL;
const walletAddress = process.argv[2];
const baseName = process.argv[3];
const baseUsername = process.argv[4];
if (!apiUrl) { console.log("ERROR: DFM_API_URL not set"); process.exit(1); }
if (!walletAddress || !baseName || !baseUsername) {
console.log("ERROR: usage: node profile-launch.js <walletAddress> <name> <username>");
process.exit(1);
}
// 从DFM_AGENT_KEYPAIR派生代理钱包公钥
const agentKeypair = Keypair.fromSecretKey(bs58.decode(process.env.DFM_AGENT_KEYPAIR));
const agentWalletAddress = agentKeypair.publicKey.toBase58();
const MAX_ATTEMPTS = 5;
const UNAME_RX = /username/i; // 区分用户名重复与钱包已有代理的情况
// 将基础用户名清理为允许的字符集(字母数字+下划线)
const sanitize = (s) => s.toLowerCase().replace(/[^a-z0-9_]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "");
const cleanBase = sanitize(baseUsername) || "agent";
// 后缀生成器:看似确定但唯一。4位十六进制字符 = 65k组合。
const suffix = () => crypto.randomBytes(2).toString("hex");
function attempt(usernameToTry, agentNameToTry, n) {
const payload = JSON.stringify({
userPublicKey: walletAddress,
agentWalletAddress: agentWalletAddress,
name: agentNameToTry,
username: usernameToTry,
metadata: [{ key: "created_by", value: "dfm-agent-skill" }]
});
const url = new URL(apiUrl + "/api/v2/agent/profile-launch");
const client = url.protocol === "https:" ? https : http;
const req = client.request(url, {
method: "POST",
headers: { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(payload) }
}, (res) => {
let data = "";
res.on("data", (chunk) => data += chunk);
res.on("end", () => {
let json = null;
try { json = JSON.parse(data); } catch { /* 继续执行 */ }
const message = json?.message || json?.error || data;
// 409 + 用户名重复 -> 重新生成用户名,重试。请勿在"此钱包已存在代理配置文件"时重试——此流程下无法恢复。
if (res.statusCode === 409 && UNAME_RX.test(String(message)) && !/wallet/i.test(String(message))) {
if (n >= MAX_ATTEMPTS) {
console.log("ERROR: username conflicts after " + MAX_ATTEMPTS + " attempts. Last tried: " + usernameToTry);
process.exit(1);
}
const nextUsername = cleanBase + "_" + suffix();
const nextName = baseName; // 保留显示名称;仅用户名需要唯一
console.log("RETRY=username_taken attempt=" + (n + 1) + " next_username=" + nextUsername);
return attempt(nextUsername, nextName, n + 1);
}
if (res.statusCode === 409) {
console.log("ERROR: 409 Conflict (not username-related): " + message);
process.exit(1);
}
if (res.statusCode < 200 || res.statusCode >= 300) {
console.log("ERROR: HTTP " + res.statusCode + ": " + message);
process.exit(1);
}
const token = json?.data?.token?.token;
if (!token) { console.log("ERROR: No token in response. Response: " + data); process.exit(1); }
// 写入.claude/settings.json
const sp = path.join(process.cwd(), ".claude", "settings.json");
let s = {}; try { s = JSON.parse(fs.readFileSync(sp, "utf8")); } catch {}
if (!s.env) s.env = {};
s.env.DFM_AUTH_TOKEN = token;
fs.writeFileSync(sp, JSON.stringify(s, null, 2));
// 替换~/.zshrc中任何现有的DFM_AUTH_TOKEN导出;绝不追加重复项。
const zshrcPath = path.join(os.homedir(), ".zshrc");
let zshrc = "";
try { zshrc = fs.readFileSync(zshrcPath, "utf8"); } catch {}
const newLine = "export DFM_AUTH_TOKEN=\"" + token + "\"";
const lineRe = /^\s*export\s+DFM_AUTH_TOKEN=.*$/gm;
if (lineRe.test(zshrc)) {
zshrc = zshrc.replace(lineRe, newLine);
} else {
if (zshrc.length && !zshrc.endsWith("\n")) zshrc += "\n";
zshrc += newLine + "\n";
}
fs.writeFileSync(zshrcPath, zshrc);
// 仅输出安全信息——绝不输出令牌
const profileName = json.data?.agentProfile?.name || agentNameToTry;
const profileUsername = json.data?.agentProfile?.username || usernameToTry;
console.log("STATUS=success");
console.log("AGENT_NAME=" + profileName);
console.log("AGENT_USERNAME=" + profileUsername);
console.log("AGENT_WALLET=" + agentWalletAddress);
console.log("ATTEMPTS=" + n);
console.log("DFM_AUTH_TOKEN=set");
});
});
req.on("error", (e) => { console.log("ERROR: " + e.message); process.exit(1); });
req.write(payload);
req.end();
}
attempt(cleanBase, baseName, 1);node .claude/profile-launch.js <WALLET_ADDRESS> "<AGENT_NAME>" "<AGENT_USERNAME>"DFM_AGENT_KEYPAIRagentWalletAddressSTATUS=successAGENT_NAME=...AGENT_USERNAME=...AGENT_WALLET=...ATTEMPTS=<n>DFM_AUTH_TOKEN=set.claude/settings.json~/.zshrc409"Username is already taken"POST /profile-launchRETRY=username_taken attempt=<n> next_username=<new>nameusername409"An agent profile already exists for this wallet address"/token/refresh-by-walletERROR:DFM_AGENT_KEYPAIR.claude/settings.json.claude/settings.jsonWebSearchWebFetchWebSearchWebFetchmintAddressunderlyingAssetsWebSearchWebFetchWebSearchWebFetchmintAddressunderlyingAssets"DTF""YIELD_DTF"dtf-createsymbolnamemintAddressasset-allocationlogoUrlbannerUrlmetadataUri"DTF""YIELD_DTF"dtf-createsymbolnameasset-allocationmintAddresslogoUrlbannerUrlmetadataUriPOST {DFM_API_URL}/api/v2/agent/launch-dtf{
"signerPublicKey": "<public key derived from DFM_AGENT_KEYPAIR>",
"vaultName": "Solana Blue Chips",
"vaultSymbol": "SOLBC",
"underlyingAssets": [
{ "symbol": "SOL", "mintBps": 4000 },
{ "symbol": "JUP", "mintBps": 3000 },
{ "name": "Bonk", "mintBps": 3000 }
],
"managementFees": 200,
"category": 0,
"threshold": 500
}{
"onChain": {
"transaction": "base64-encoded-unsigned-versioned-transaction...",
"vaultIndex": 42,
"vaultPda": "7Xk...def",
"vaultMintPda": "9Rm...ghi"
}
}POST {DFM_API_URL}/api/v2/agent/launch-dtf{
"signerPublicKey": "<从DFM_AGENT_KEYPAIR派生的公钥>",
"vaultName": "Solana Blue Chips",
"vaultSymbol": "SOLBC",
"underlyingAssets": [
{ "symbol": "SOL", "mintBps": 4000 },
{ "symbol": "JUP", "mintBps": 3000 },
{ "name": "Bonk", "mintBps": 3000 }
],
"managementFees": 200,
"category": 0,
"threshold": 500
}{
"onChain": {
"transaction": "base64-encoded-unsigned-versioned-transaction...",
"vaultIndex": 42,
"vaultPda": "7Xk...def",
"vaultMintPda": "9Rm...ghi"
}
}import { Keypair, VersionedTransaction, Connection } from "@solana/web3.js";
const bs58 = require("bs58").default || require("bs58");
const keypair = Keypair.fromSecretKey(bs58.decode(process.env.DFM_AGENT_KEYPAIR!));
const connection = new Connection(process.env.SOLANA_RPC_URL || "https://api.mainnet-beta.solana.com");
// Deserialize the unsigned transaction from the API response
const txBytes = Buffer.from(response.onChain.transaction, "base64");
const tx = VersionedTransaction.deserialize(txBytes);
// Sign with the agent's keypair
tx.sign([keypair]);
// Submit on-chain
const signature = await connection.sendRawTransaction(tx.serialize(), {
skipPreflight: false,
preflightCommitment: "confirmed",
});
await connection.confirmTransaction(signature, "confirmed");import { Keypair, VersionedTransaction, Connection } from "@solana/web3.js";
const bs58 = require("bs58").default || require("bs58");
const keypair = Keypair.fromSecretKey(bs58.decode(process.env.DFM_AGENT_KEYPAIR!));
const connection = new Connection(process.env.SOLANA_RPC_URL || "https://api.mainnet-beta.solana.com");
// 从API响应反序列化未签名交易
const txBytes = Buffer.from(response.onChain.transaction, "base64");
const tx = VersionedTransaction.deserialize(txBytes);
// 使用代理密钥对签名
tx.sign([keypair]);
// 提交到链上
const signature = await connection.sendRawTransaction(tx.serialize(), {
skipPreflight: false,
preflightCommitment: "confirmed",
});
await connection.confirmTransaction(signature, "confirmed");POST {DFM_API_URL}/api/v2/agent/dtf-createdtf-create{
"transactionSignature": "<signature from step 3b>",
"vaultName": "Solana Blue Chips",
"vaultSymbol": "SOLBC",
"vaultType": "DTF",
"description": "Top-tier Solana ecosystem tokens",
"tags": ["Blue Chip", "Solana", "DeFi"],
"logoUrl": "",
"bannerUrl": "",
"asset_mode": "OPEN",
"asset_whitelist": [],
"asset_blacklist": [],
"min_amm_liquidity_usd": 100000,
"min_24h_volume_usd": 500000,
"min_assets": 3,
"max_assets": 12,
"max_asset_pct": 4000,
"min_asset_pct": 500,
"min_stablecoin_pct": 0,
"max_rebalance_pct": 2500,
"min_rebalance_interval_hours": 4,
"max_rebalances_per_day": 3,
"max_rebalances_per_week": 14,
"launch_blackout_hours": 24,
"fee_locked": true,
"notes": "Auto-generated policy for blue chip strategy"
}{
"transactionSignature": "<signature>",
"vaultName": "Solana LST Yield",
"vaultSymbol": "SLSTY",
"vaultType": "YIELD_DTF",
"description": "Diversified Solana liquid staking token yield fund",
"tags": ["Yield", "LST", "Staking", "Solana"],
"logoUrl": "",
"bannerUrl": "",
"asset_mode": "WHITELIST_ONLY",
"asset_whitelist": ["mSoLzYCxHdYgdzU16g5QSh3i5K3z3KZK7ytfqcJm7So", "J1toso1uCk3RLmjorhTtrVwY9HJ7X8V9yYac6Y7kGCPn", "bSo13r4TkiE4KumL71LsHTPpL2euBYLFx6h9HP3piy1"],
"asset_blacklist": [],
"min_amm_liquidity_usd": 500000,
"min_24h_volume_usd": 500000,
"min_assets": 2,
"max_assets": 8,
"max_asset_pct": 5000,
"min_asset_pct": 1000,
"min_stablecoin_pct": 0,
"max_rebalance_pct": 2000,
"min_rebalance_interval_hours": 12,
"max_rebalances_per_day": 1,
"max_rebalances_per_week": 5,
"launch_blackout_hours": 24,
"fee_locked": true,
"notes": "Whitelisted LST-only yield fund — only approved liquid staking tokens allowed"
}POST {DFM_API_URL}/api/v2/agent/dtf-createdtf-create{
"transactionSignature": "<步骤3b中的签名>",
"vaultName": "Solana Blue Chips",
"vaultSymbol": "SOLBC",
"vaultType": "DTF",
"description": "顶级Solana生态系统代币",
"tags": ["Blue Chip", "Solana", "DeFi"],
"logoUrl": "",
"bannerUrl": "",
"asset_mode": "OPEN",
"asset_whitelist": [],
"asset_blacklist": [],
"min_amm_liquidity_usd": 100000,
"min_24h_volume_usd": 500000,
"min_assets": 3,
"max_assets": 12,
"max_asset_pct": 4000,
"min_asset_pct": 500,
"min_stablecoin_pct": 0,
"max_rebalance_pct": 2500,
"min_rebalance_interval_hours": 4,
"max_rebalances_per_day": 3,
"max_rebalances_per_week": 14,
"launch_blackout_hours": 24,
"fee_locked": true,
"notes": "蓝筹策略自动生成的策略"
}{
"transactionSignature": "<签名>",
"vaultName": "Solana LST Yield",
"vaultSymbol": "SLSTY",
"vaultType": "YIELD_DTF",
"description": "多元化Solana流动性质押代币收益基金",
"tags": ["Yield", "LST", "Staking", "Solana"],
"logoUrl": "",
"bannerUrl": "",
"asset_mode": "WHITELIST_ONLY",
"asset_whitelist": ["mSoLzYCxHdYgdzU16g5QSh3i5K3z3KZK7ytfqcJm7So", "J1toso1uCk3RLmjorhTtrVwY9HJ7X8V9yYac6Y7kGCPn", "bSo13r4TkiE4KumL71LsHTPpL2euBYLFx6h9HP3piy1"],
"asset_blacklist": [],
"min_amm_liquidity_usd": 500000,
"min_24h_volume_usd": 500000,
"min_assets": 2,
"max_assets": 8,
"max_asset_pct": 5000,
"min_asset_pct": 1000,
"min_stablecoin_pct": 0,
"max_rebalance_pct": 2000,
"min_rebalance_interval_hours": 12,
"max_rebalances_per_day": 1,
"max_rebalances_per_week": 5,
"launch_blackout_hours": 24,
"fee_locked": true,
"notes": "白名单LST专属收益基金——仅允许已批准的流动性质押代币"
}| Strategy Type | | | | | | |
|---|---|---|---|---|---|---|
| Conservative (blue chip, index) | 3000-4000 | 500-1000 | 500000 | 1000000 | 2 | 6 |
| Moderate (mixed, ecosystem) | 4000-5000 | 500 | 100000 | 500000 | 3 | 4 |
| Aggressive (meme, trending) | 5000-6000 | 300 | 50000 | 100000 | 4 | 2 |
| Yield (LSTs, staking, yield) | 4000-5000 | 500-1000 | 500000 | 500000 | 1 | 12 |
asset_mode"OPEN""WHITELIST_ONLY"asset_whitelistasset_whitelist"OPEN_BLACKLIST"asset_blacklistasset_blacklist"WHITELIST_BLACKLIST"asset_whitelistasset_blacklistWHITELIST_ONLYOPENOPEN_BLACKLISTmin_assetsmax_assets12max_rebalance_pct20003000max_rebalances_per_weekmax_rebalances_per_day * 7launch_blackout_hours24fee_lockedtruenotes| 策略类型 | | | | | | |
|---|---|---|---|---|---|---|
| 保守型(蓝筹、指数) | 3000-4000 | 500-1000 | 500000 | 1000000 | 2 | 6 |
| 稳健型(混合、生态系统) | 4000-5000 | 500 | 100000 | 500000 | 3 | 4 |
| 激进型(模因、热门) | 5000-6000 | 300 | 50000 | 100000 | 4 | 2 |
| 收益型(LST、质押、收益) | 4000-5000 | 500-1000 | 500000 | 500000 | 1 | 12 |
asset_mode"OPEN""WHITELIST_ONLY"asset_whitelistasset_whitelist"OPEN_BLACKLIST"asset_blacklistasset_blacklist"WHITELIST_BLACKLIST"asset_whitelistasset_blacklistWHITELIST_ONLYOPENOPEN_BLACKLISTmin_assetsmax_assets12max_rebalance_pct20003000max_rebalances_per_weekmax_rebalances_per_day * 7launch_blackout_hours24fee_lockedtruenotescategory: 0underlyingAssetssymbolnamemintAddressasset-allocationsymbol: "USDC"name: "USD Coin"underlyingAssetslaunch-dtfunderlyingAssetsdtf-createvaultType"DTF""YIELD_DTF"logoUrlbannerUrlvaultNamevaultSymbollaunch-dtfcategory: 0underlyingAssetssymbolnameasset-allocationmintAddressunderlyingAssetssymbol: "USDC"name: "USD Coin"launch-dtfunderlyingAssetsdtf-createvaultType"DTF""YIELD_DTF"logoUrlbannerUrlvaultNamevaultSymbollaunch-dtflaunch-dtfdistribute-feesVersionedTransactionimport { Keypair, VersionedTransaction, Connection } from "@solana/web3.js";
const bs58 = require("bs58").default || require("bs58");
async function signAndSendTransaction(
base64Tx: string,
keypair: Keypair,
connection: Connection
): Promise<string> {
const txBytes = Buffer.from(base64Tx, "base64");
const tx = VersionedTransaction.deserialize(txBytes);
tx.sign([keypair]);
const signature = await connection.sendRawTransaction(tx.serialize(), {
skipPreflight: false,
preflightCommitment: "confirmed",
});
await connection.confirmTransaction(signature, "confirmed");
return signature;
}
// Usage:
const keypair = Keypair.fromSecretKey(bs58.decode(process.env.DFM_AGENT_KEYPAIR!));
const connection = new Connection(process.env.SOLANA_RPC_URL || "https://api.mainnet-beta.solana.com");
const sig = await signAndSendTransaction(response.onChain.transaction, keypair, connection);launch-dtfdtf-createdtf-createlaunch-dtflaunch-dtflaunch-dtflaunch-dtfdtf-createdtf-createtransactionSignaturevaultNamevaultSymbollaunch-dtfdtf-createlaunch-dtfdistribute-feesVersionedTransactionimport { Keypair, VersionedTransaction, Connection } from "@solana/web3.js";
const bs58 = require("bs58").default || require("bs58");
async function signAndSendTransaction(
base64Tx: string,
keypair: Keypair,
connection: Connection
): Promise<string> {
const txBytes = Buffer.from(base64Tx, "base64");
const tx = VersionedTransaction.deserialize(txBytes);
tx.sign([keypair]);
const signature = await connection.sendRawTransaction(tx.serialize(), {
skipPreflight: false,
preflightCommitment: "confirmed",
});
await connection.confirmTransaction(signature, "confirmed");
return signature;
}
// 使用示例:
const keypair = Keypair.fromSecretKey(bs58.decode(process.env.DFM_AGENT_KEYPAIR!));
const connection = new Connection(process.env.SOLANA_RPC_URL || "https://api.mainnet-beta.solana.com");
const sig = await signAndSendTransaction(response.onChain.transaction, keypair, connection);launch-dtfdtf-createdtf-createlaunch-dtflaunch-dtflaunch-dtflaunch-dtfdtf-createtransactionSignaturevaultNamevaultSymboldtf-createlaunch-dtfdtf-createGET {DFM_API_URL}/api/v2/agent/dtf/:symbol/stateGET {DFM_API_URL}/api/v2/agent/dtf/:symbol/rebalance/checkPOST {DFM_API_URL}/api/v2/agent/dtf/:symbol/rebalancePOST {DFM_API_URL}/api/v2/agent/dtf/:symbol/distribute-feesGET {DFM_API_URL}/api/v2/agent/dtf/:symbol/stateGET {DFM_API_URL}/api/v2/agent/dtf/:symbol/rebalance/checkPOST {DFM_API_URL}/api/v2/agent/dtf/:symbol/rebalancePOST {DFM_API_URL}/api/v2/agent/dtf/:symbol/distribute-fees/rebalance/check/rebalance200policyCheck{
"policyCheck": {
"ok": true,
"flagged": true,
"reviewFlags": [
{ "violationCode": "rule5MaxPctPerAsset", "mint": "JUP...", "message": "...", "details": {...} },
{ "violationCode": "rule7MinStablecoinFloor", "message": "...", "details": null }
],
"violations": [ ... ]
},
"suggestion": { ... } // present on /rebalance/check
// upfrontFeeSol, actualFeesSol present on /rebalance
}policyReviewFlagRebalancingSuggestionpolicyCheck.flaggedtruereviewFlagsviolationCodemintmessagedetailsflagged: true/rebalance/check/rebalance200policyCheck{
"policyCheck": {
"ok": true,
"flagged": true,
"reviewFlags": [
{ "violationCode": "rule5MaxPctPerAsset", "mint": "JUP...", "message": "...", "details": {...} },
{ "violationCode": "rule7MinStablecoinFloor", "message": "...", "details": null }
],
"violations": [ ... ]
},
"suggestion": { ... } // 在/rebalance/check端点存在
// upfrontFeeSol、actualFeesSol在/rebalance端点存在
}policyReviewFlagRebalancingSuggestionpolicyCheck.flaggedtruereviewFlagsviolationCodemintmessagedetailsflagged: truelaunch-dtfdtf-createmetadataUrilogoUrlbannerUrllaunch-dtfunderlyingAssetstimeout: 600000launch-dtfdtf-createmetadataUrilogoUrlbannerUrllaunch-dtfunderlyingAssetstimeout: 600000DFM_AUTH_TOKENDFM_AGENT_KEYPAIR~/.zshrcWebSearchWebFetchpolicyCheck.flaggedUSDCUSD CoinunderlyingAssetsDFM_AUTH_TOKENDFM_AGENT_KEYPAIR~/.zshrcWebSearchWebFetchpolicyCheck.flaggedunderlyingAssetsUSDCUSD Coinnpm install @solana/web3.jsnpm install bs58npm install @solana/web3.jsnpm install bs58undefinedundefined
> **Note:** `DFM_AUTH_TOKEN` and `DFM_AGENT_KEYPAIR` are set automatically by the agent during first use. You do NOT need to set them manually.
> **注意:** `DFM_AUTH_TOKEN`和`DFM_AGENT_KEYPAIR`会在首次使用时由Agent自动设置。您无需手动设置。npx skills add DFM-Finance/DFM-AgentSkillsmkdir -p .claude/skills
cp -r .agents/skills/dfm-agent .claude/skills/dfm-agentnpx skills add DFM-Finance/DFM-AgentSkillsmkdir -p .claude/skills
cp -r .agents/skills/dfm-agent .claude/skills/dfm-agentPOST /profile-launch.claude/settings.json~/.zshrcAGENT_WALLET_PATHDFM_AGENT_KEYPAIR~/.zshrcPOST /profile-launch.claude/settings.json~/.zshrcAGENT_WALLET_PATHDFM_AGENT_KEYPAIR~/.zshrc{DFM_API_URL}/api/v2/agent/...Authorization: Bearer <DFM_AUTH_TOKEN>profile-launchlaunch-dtfdistribute-feesDFM_AGENT_KEYPAIRPOST /profile-launch{DFM_API_URL}/api/v2/agent/...Authorization: Bearer <DFM_AUTH_TOKEN>profile-launchlaunch-dtfdistribute-feesDFM_AGENT_KEYPAIRPOST /profile-launchAGENT_WALLET_PATHSOLANA_KEYPAIR_PATHWALLET_OUTPUT_PATH<project-root>/solana-keypair/keypair.jsonAGENT_WALLET_PATHSOLANA_KEYPAIR_PATHWALLET_OUTPUT_PATH<project-root>/solana-keypair/keypair.jsonimport { Keypair } from "@solana/web3.js";
const bs58 = require("bs58").default || require("bs58");
import * as fs from "fs";
import * as path from "path";
import * as os from "os";
// 1. Resolve output path
const outPath = path.resolve(
process.env.AGENT_WALLET_PATH ??
process.env.SOLANA_KEYPAIR_PATH ??
process.env.WALLET_OUTPUT_PATH ??
path.join(os.homedir(), ".dfm", "agent-wallet.json")
);
// 2. Generate keypair and save to file
fs.mkdirSync(path.dirname(outPath), { recursive: true });
const keypair = Keypair.generate();
fs.writeFileSync(outPath, JSON.stringify(Array.from(keypair.secretKey)), { mode: 0o600 });
// 3. Write base58 secret key directly to shell profile (NEVER print to terminal)
const base58Secret = bs58.encode(keypair.secretKey);
const shellProfile = path.join(os.homedir(), ".zshrc");
fs.appendFileSync(shellProfile, `\nexport DFM_AGENT_KEYPAIR="${base58Secret}"\n`);
// 4. Only output the PUBLIC KEY
const pubkey = keypair.publicKey.toBase58();
console.log(`PUBLIC_KEY=${pubkey}`);
console.log(`WALLET_PATH=${outPath}`);
// NEVER console.log the base58Secret — it was written to ~/.zshrc silentlybase58Secret~/.zshrcfs.appendFileSyncimport { Keypair } from "@solana/web3.js";
const bs58 = require("bs58").default || require("bs58");
import * as fs from "fs";
import * as path from "path";
import * as os from "os";
// 1. 解析输出路径
const outPath = path.resolve(
process.env.AGENT_WALLET_PATH ??
process.env.SOLANA_KEYPAIR_PATH ??
process.env.WALLET_OUTPUT_PATH ??
path.join(os.homedir(), ".dfm", "agent-wallet.json")
);
// 2. 生成密钥对并保存到文件
fs.mkdirSync(path.dirname(outPath), { recursive: true });
const keypair = Keypair.generate();
fs.writeFileSync(outPath, JSON.stringify(Array.from(keypair.secretKey)), { mode: 0o600 });
// 3. 将base58格式密钥直接写入Shell配置文件(绝不打印到终端)
const base58Secret = bs58.encode(keypair.secretKey);
const shellProfile = path.join(os.homedir(), ".zshrc");
fs.appendFileSync(shellProfile, `\nexport DFM_AGENT_KEYPAIR="${base58Secret}"\n`);
// 4. 仅输出公钥
const pubkey = keypair.publicKey.toBase58();
console.log(`PUBLIC_KEY=${pubkey}`);
console.log(`WALLET_PATH=${outPath}`);
// 绝不要console.log base58Secret — 它已静默写入~/.zshrcbase58Secretfs.appendFileSync~/.zshrcimport { Keypair } from "@solana/web3.js";
const bs58 = require("bs58").default || require("bs58");
const keypair = Keypair.fromSecretKey(bs58.decode(process.env.DFM_AGENT_KEYPAIR!));
const signerPublicKey = keypair.publicKey.toBase58();
// Use signerPublicKey in API request bodiesimport { Keypair } from "@solana/web3.js";
const bs58 = require("bs58").default || require("bs58");
const keypair = Keypair.fromSecretKey(bs58.decode(process.env.DFM_AGENT_KEYPAIR!));
const signerPublicKey = keypair.publicKey.toBase58();
// 在API请求体中使用signerPublicKeyFor full request/response schemas, seereferences/api-reference.md
| Action | Method | Endpoint | Auth | Body |
|---|---|---|---|---|
| Launch agent profile | | | No | |
| Build vault tx | | | JWT | |
| Create DTF (policy + DB) | | | JWT | |
| My vaults | | | JWT | - |
| Vault state | | | JWT | - |
| Vault policy | | | JWT | - |
| Rebalance check | | | JWT | - |
| Rebalance | | | JWT | |
| Build distribute fees tx | | | JWT | |
| Revoke token | | | JWT | - |
| Refresh token (by profileId) | | | No | |
| Refresh token (by wallet) | | | No | |
完整请求/响应架构请参阅references/api-reference.md
| 操作 | 方法 | 端点 | 认证 | 请求体 |
|---|---|---|---|---|
| 启动代理配置文件 | | | 否 | |
| 构建金库交易 | | | JWT | |
| 创建DTF(策略+数据库) | | | JWT | |
| 我的金库 | | | JWT | - |
| 金库状态 | | | JWT | - |
| 金库策略 | | | JWT | - |
| 重新平衡检查 | | | JWT | - |
| 重新平衡 | | | JWT | |
| 构建费用分配交易 | | | JWT | |
| 吊销令牌 | | | JWT | - |
| 刷新令牌(通过profileId) | | | 否 | |
| 刷新令牌(通过钱包) | | | 否 | |
launch-dtfdtf-createmetadataUri: ""logoUrl: ""bannerUrl: ""launch-dtfdtf-createmetadataUri: ""logoUrl: ""bannerUrl: ""| What you say | What the agent does |
|---|---|
| Asks for wallet address, creates agent profile via |
| Researches top SOL tokens, picks name/symbol/allocations/policy, builds tx, signs, submits, creates policy |
| Finds trending meme tokens, builds diversified allocation, sets policy, deploys |
| |
| Checks policy, triggers server-side rebalance if approved |
| |
| Creates keypair, saves to file, writes env var, reports public key only |
| 您的指令 | Agent执行的操作 |
|---|---|
| 询问钱包地址,通过 |
| 研究顶级SOL代币,选择名称/符号/配置/策略,构建交易,签名,提交,创建策略 |
| 查找热门模因代币,构建多元化配置,设置策略,部署 |
| |
| 检查策略,若批准则触发服务器端重新平衡 |
| |
| 创建密钥对,保存到文件,写入环境变量,仅报告公钥 |
| Problem | Fix |
|---|---|
| "Unauthorized" errors | Use the token refresh script in the Pre-flight section ( |
| "Keypair file not found" | Re-generate wallet (Step 4). Check: |
| "No signer keypair" / empty DFM_AGENT_KEYPAIR | |
| Transaction fails on-chain | Agent Wallet needs SOL for tx fees + USDC for vault creation fee. Fund the wallet first. |
Policy | Rebalance is non-blocking — the operation already proceeded. Inspect |
| Token revoked unexpectedly | Tokens are only invalidated by an explicit |
| 409 Conflict on dtf-create | A policy already exists for this vault name/symbol. Use a unique name and symbol. |
| 409 "Username is already taken" on profile-launch | The |
| 409 "An agent profile already exists for this wallet address" on profile-launch | The wallet has already been onboarded — do NOT call |
| 问题 | 解决方法 |
|---|---|
| "Unauthorized"错误 | 使用飞行前部分的令牌刷新脚本( |
| "Keypair file not found" | 重新生成钱包(步骤4)。检查: |
| "No signer keypair" / DFM_AGENT_KEYPAIR为空 | |
| 链上交易失败 | 代理钱包需要SOL支付交易费用 + USDC支付金库创建费用。先为钱包充值。 |
重新平衡时策略 | 重新平衡是非阻塞的——操作已执行。检查 |
| 令牌意外吊销 | 令牌仅会因显式 |
| dtf-create时409 Conflict | 此金库名称/符号的策略已存在。使用唯一名称和符号。 |
| profile-launch时409 "Username is already taken" | |
| profile-launch时409 "An agent profile already exists for this wallet address" | 该钱包已完成注册——不要再次调用 |
DFM_AUTH_TOKENDFM_AGENT_KEYPAIR0o600DFM_AGENT_KEYPAIRDFM_AUTH_TOKENDFM_AUTH_TOKENDFM_AGENT_KEYPAIR0o600DFM_AGENT_KEYPAIRDFM_AUTH_TOKEN