Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,787 changes: 846 additions & 941 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@
"@types/node": "^22.0.0",
"@types/qrcode-terminal": "^0.12.2",
"typescript": "^5.6.0",
"vitest": "^2.1.0"
"vitest": "^4.1.4"
},
"engines": {
"node": ">=18.17.0"
Expand Down
5 changes: 4 additions & 1 deletion src/config/chains.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,11 @@ export function validateRpcUrl(chain: SupportedChain, url: string): void {
try {
parsed = new URL(url);
} catch {
// Do NOT echo the URL back — configured RPC URLs often contain a provider
// API key in the path (e.g. .../v3/<key>). A malformed URL may still carry
// one, and error messages end up in logs / stderr where keys leak.
throw new RpcConfigError(
`RPC URL for ${chain} is not a valid URL: ${url}. Fix it via \`vaultpilot-mcp-setup\` or the relevant env var.`
`RPC URL for ${chain} is not a valid URL. Fix it via \`vaultpilot-mcp-setup\` or the relevant env var.`
);
}
if (parsed.protocol !== "https:") {
Expand Down
4 changes: 3 additions & 1 deletion src/modules/feedback/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ export async function requestCapability(args: RequestCapabilityArgs) {
throw err;
}

const title = `[agent-request] ${summary}`;
// Titles parse @-mentions too — a prompt-injected summary containing
// `@someuser` would ping arbitrary GitHub users when the issue is opened.
const title = `[agent-request] ${neutralizeMentions(summary)}`;
const body = buildIssueBody({ description, category, context, agentName });
const labels = [ISSUE_LABEL, category].filter((v): v is string => Boolean(v));
const payload: IssuePayload = { title, body, labels };
Expand Down
47 changes: 47 additions & 0 deletions src/modules/tron/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
import { resolveTronApiKey, readUserConfig } from "../../config/user-config.js";
import { issueTronHandle } from "../../signing/tron-tx-store.js";
import { encodeTrc20TransferParam } from "./address.js";
import { assertTronRawDataMatches } from "./verify-raw-data.js";
import type { UnsignedTronTx } from "../../types/index.js";

/**
Expand Down Expand Up @@ -134,6 +135,13 @@ export async function buildTronNativeSend(
throw new Error("TronGrid createtransaction returned no transaction — unexpected shape.");
}

assertTronRawDataMatches(res.raw_data_hex, {
kind: "native_send",
from: args.from,
to: args.to,
amountSun,
});

const tx: UnsignedTronTx = {
chain: "tron",
action: "native_send",
Expand Down Expand Up @@ -223,6 +231,15 @@ export async function buildTronTokenSend(
throw new Error("TronGrid triggersmartcontract returned no transaction — unexpected shape.");
}

assertTronRawDataMatches(ttx.raw_data_hex, {
kind: "trc20_send",
from: args.from,
contract: args.token,
parameterHex: parameter,
feeLimitSun,
callValue: 0n,
});

const tx: UnsignedTronTx = {
chain: "tron",
action: "trc20_send",
Expand Down Expand Up @@ -319,6 +336,12 @@ export async function buildTronVote(args: BuildTronVoteArgs): Promise<UnsignedTr
args.votes.length === 1 ? "" : "s"
} (replaces any prior votes)`;

assertTronRawDataMatches(res.raw_data_hex, {
kind: "vote",
from: args.from,
votes: args.votes.map((v) => ({ address: v.address, count: v.count })),
});

const tx: UnsignedTronTx = {
chain: "tron",
action: "vote",
Expand Down Expand Up @@ -384,6 +407,13 @@ export async function buildTronFreeze(
throw new Error("TronGrid freezebalancev2 returned no transaction — unexpected shape.");
}

assertTronRawDataMatches(res.raw_data_hex, {
kind: "freeze",
from: args.from,
frozenBalanceSun: amountSun,
resource: args.resource,
});

const tx: UnsignedTronTx = {
chain: "tron",
action: "freeze",
Expand Down Expand Up @@ -439,6 +469,13 @@ export async function buildTronUnfreeze(
throw new Error("TronGrid unfreezebalancev2 returned no transaction — unexpected shape.");
}

assertTronRawDataMatches(res.raw_data_hex, {
kind: "unfreeze",
from: args.from,
unfreezeBalanceSun: amountSun,
resource: args.resource,
});

const tx: UnsignedTronTx = {
chain: "tron",
action: "unfreeze",
Expand Down Expand Up @@ -481,6 +518,11 @@ export async function buildTronWithdrawExpireUnfreeze(
throw new Error("TronGrid withdrawexpireunfreeze returned no transaction — unexpected shape.");
}

assertTronRawDataMatches(res.raw_data_hex, {
kind: "withdraw_expire_unfreeze",
from: args.from,
});

const tx: UnsignedTronTx = {
chain: "tron",
action: "withdraw_expire_unfreeze",
Expand Down Expand Up @@ -525,6 +567,11 @@ export async function buildTronClaimRewards(
throw new Error("TronGrid withdrawbalance returned no transaction — unexpected shape.");
}

assertTronRawDataMatches(res.raw_data_hex, {
kind: "claim_rewards",
from: args.from,
});

const tx: UnsignedTronTx = {
chain: "tron",
action: "claim_rewards",
Expand Down
Loading
Loading