Agent-facing strings: AP3X terminology, working Koios default, vocabulary cleanup
- ADA -> AP3X in tool/param descriptions, outputs, and safety messages - default testnet Koios URL -> v2 endpoint (old default returns 503) - self-improvement descriptions: improvement proposals / proposal metrics - em dashes -> hyphens in strings and comments No logic changes; code identifiers and protocol field names untouched. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
|
||||
# Vector Network Endpoints
|
||||
VECTOR_OGMIOS_URL=https://ogmios.vector.testnet.apexfusion.org
|
||||
VECTOR_KOIOS_URL=https://koios.vector.testnet.apexfusion.org/
|
||||
VECTOR_KOIOS_URL=https://v2.koios.vector.testnet.apexfusion.org/
|
||||
VECTOR_SUBMIT_URL=https://submit.vector.testnet.apexfusion.org/api/submit/tx
|
||||
VECTOR_EXPLORER_URL=https://vector.testnet.apexscan.org
|
||||
|
||||
|
||||
@@ -13,14 +13,14 @@ const envPath = resolve(__dirname, '../.env');
|
||||
console.error('Looking for .env file at:', envPath);
|
||||
console.error('.env file exists:', existsSync(envPath));
|
||||
|
||||
// Load environment variables (non-fatal if missing — env vars can be passed directly)
|
||||
// Load environment variables (non-fatal if missing - env vars can be passed directly)
|
||||
if (existsSync(envPath)) {
|
||||
const result = config({ path: envPath });
|
||||
if (result.error) {
|
||||
console.error('Warning: Error loading .env file:', result.error);
|
||||
}
|
||||
} else {
|
||||
console.error('No .env file found — using environment variables directly');
|
||||
console.error('No .env file found - using environment variables directly');
|
||||
}
|
||||
|
||||
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -162,7 +162,7 @@ export class OgmiosProvider implements Provider {
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
// Koios unreachable — caller can fall back to scanning UTxOs
|
||||
// Koios unreachable - caller can fall back to scanning UTxOs
|
||||
}
|
||||
}
|
||||
|
||||
@@ -310,7 +310,7 @@ export class OgmiosProvider implements Provider {
|
||||
|
||||
/**
|
||||
* Evaluate a transaction without submitting (dry run).
|
||||
* Backward-compatible alias — returns raw Ogmios response.
|
||||
* Backward-compatible alias - returns raw Ogmios response.
|
||||
*/
|
||||
async evaluateTransaction(txCborHex: string): Promise<any> {
|
||||
return this.rpc('evaluateTransaction', {
|
||||
|
||||
@@ -3,8 +3,8 @@ import { resolve, dirname } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import type { SpendLimits, SpendStatus, AuditEntry } from './types.js';
|
||||
|
||||
const VECTOR_SPEND_LIMIT_PER_TX = parseInt(process.env.VECTOR_SPEND_LIMIT_PER_TX || '100000000'); // 100 ADA
|
||||
const VECTOR_SPEND_LIMIT_DAILY = parseInt(process.env.VECTOR_SPEND_LIMIT_DAILY || '500000000'); // 500 ADA
|
||||
const VECTOR_SPEND_LIMIT_PER_TX = parseInt(process.env.VECTOR_SPEND_LIMIT_PER_TX || '100000000'); // 100 AP3X
|
||||
const VECTOR_SPEND_LIMIT_DAILY = parseInt(process.env.VECTOR_SPEND_LIMIT_DAILY || '500000000'); // 500 AP3X
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
@@ -91,7 +91,7 @@ export class SafetyLayer {
|
||||
if (amountLovelace > this.limits.perTransaction) {
|
||||
return {
|
||||
allowed: false,
|
||||
reason: `Transaction amount ${(amountLovelace / 1_000_000).toFixed(6)} ADA exceeds per-transaction limit of ${(this.limits.perTransaction / 1_000_000).toFixed(6)} ADA`,
|
||||
reason: `Transaction amount ${(amountLovelace / 1_000_000).toFixed(6)} AP3X exceeds per-transaction limit of ${(this.limits.perTransaction / 1_000_000).toFixed(6)} AP3X`,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ export class SafetyLayer {
|
||||
const remaining = this.limits.daily - this.dailySpent;
|
||||
return {
|
||||
allowed: false,
|
||||
reason: `Transaction would exceed daily spend limit. Daily limit: ${(this.limits.daily / 1_000_000).toFixed(6)} ADA, already spent: ${(this.dailySpent / 1_000_000).toFixed(6)} ADA, remaining: ${(remaining / 1_000_000).toFixed(6)} ADA`,
|
||||
reason: `Transaction would exceed daily spend limit. Daily limit: ${(this.limits.daily / 1_000_000).toFixed(6)} AP3X, already spent: ${(this.dailySpent / 1_000_000).toFixed(6)} AP3X, remaining: ${(remaining / 1_000_000).toFixed(6)} AP3X`,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
import { z } from "zod";
|
||||
import { Lucid, fromText, toText, Data, Constr, credentialToAddress, getAddressDetails, SLOT_CONFIG_NETWORK } from '@lucid-evolution/lucid';
|
||||
|
||||
// Vector slot config — resolved from chain via Ogmios queryNetwork/startTime
|
||||
// Vector slot config - resolved from chain via Ogmios queryNetwork/startTime
|
||||
// Cached after first query so SLOT_CONFIG_NETWORK is set before Lucid needs it
|
||||
let vectorZeroTime: number | null = null;
|
||||
|
||||
@@ -25,13 +25,13 @@ import { rateLimiter } from './rate-limiter.js';
|
||||
// Env config
|
||||
const VECTOR_OGMIOS_URL = process.env.VECTOR_OGMIOS_URL || 'https://ogmios.vector.testnet.apexfusion.org';
|
||||
const VECTOR_SUBMIT_URL = process.env.VECTOR_SUBMIT_URL || 'https://submit.vector.testnet.apexfusion.org/api/submit/tx';
|
||||
const VECTOR_KOIOS_URL = process.env.VECTOR_KOIOS_URL || 'https://koios.vector.testnet.apexfusion.org/';
|
||||
const VECTOR_KOIOS_URL = process.env.VECTOR_KOIOS_URL || 'https://v2.koios.vector.testnet.apexfusion.org/';
|
||||
const VECTOR_EXPLORER_URL = process.env.VECTOR_EXPLORER_URL || 'https://vector.testnet.apexscan.org';
|
||||
|
||||
// Agent Registry policy ID (shared across all modules)
|
||||
const AGENT_REGISTRY_POLICY = process.env.AGENT_REGISTRY_POLICY || 'be1a0a2912da180757ed3cd61b56bb8eab0188c19dc3c0e3912d2c01';
|
||||
|
||||
// Governance contract hashes (from deploy_state.json)
|
||||
// Module contract hashes (from deploy_state.json)
|
||||
// These should be set via env vars in production
|
||||
const GOV_PROPOSAL_SPEND_HASH = process.env.GOV_PROPOSAL_SPEND_HASH || 'f815f51a76002d6a973e83fecf60f45473e040acee85c631fcce134d';
|
||||
const GOV_PROPOSAL_MINT_HASH = process.env.GOV_PROPOSAL_MINT_HASH || 'e8f38052352a3d20c5fe025e2a02d615826a154b26f2239286b8d565';
|
||||
@@ -40,11 +40,11 @@ const GOV_CRITIQUE_MINT_HASH = process.env.GOV_CRITIQUE_MINT_HASH || '2e252a8989
|
||||
const GOV_ENDORSEMENT_SPEND_HASH = process.env.GOV_ENDORSEMENT_SPEND_HASH || '5fc449848d85f30287e5bc0bd2b3e95d872ef97be27f1480c12f1a9d';
|
||||
const GOV_TREASURY_ADDRESS = process.env.GOV_TREASURY_ADDRESS || 'addr1wx434t2jc3m5uhdf7tq05xjdqu3q5z7a2lhrmn5mapsd43srh7ll8';
|
||||
|
||||
// Reference script UTxOs (CIP-33) — for validated submit
|
||||
// Reference script UTxOs (CIP-33) - for validated submit
|
||||
const GOV_PROPOSAL_SPEND_REF = process.env.GOV_PROPOSAL_SPEND_REF || 'c70a410c9c0c543a0a1103049680b89cbf6fd2277e8469c4d52632dc52b80996#0';
|
||||
const GOV_PROPOSAL_MINT_REF = process.env.GOV_PROPOSAL_MINT_REF || 'c40b64fff5c4056689354076aa3d06431d786a4f8f0c1e492e70261d2309e50a#0';
|
||||
|
||||
// Infrastructure UTxOs (governance reference inputs)
|
||||
// Infrastructure UTxOs (module reference inputs)
|
||||
const GOV_PARAMS_UTXO = process.env.GOV_PARAMS_UTXO || '2c082e833649175b4a543a5a0cf61f9b736acdfa0d315d1184645185e9a52796#0';
|
||||
const GOV_ORACLE_UTXO = process.env.GOV_ORACLE_UTXO || '7a23dfdf9468dd35cee3cad03008f2538c86834d4e5140e0ffaf2ff93e7c04a7#0';
|
||||
const GOV_CROSSREFS_UTXO = process.env.GOV_CROSSREFS_UTXO || '96a4acff8be0fb96b3839ee6c9c1fa75809b94f4967218eaf813ac56b939c4b2#0';
|
||||
@@ -276,7 +276,7 @@ export function registerSelfImprovementTools(server) {
|
||||
|
||||
server.tool(
|
||||
"vector_self_improvement_browse",
|
||||
"Browse governance proposals, critiques, and endorsements. Query on-chain UTxOs at governance script addresses and decode datums into human-readable format.",
|
||||
"Browse improvement proposals, critiques, and endorsements. Query on-chain UTxOs at the module's script addresses and decode datums into human-readable format.",
|
||||
{
|
||||
entity: z.enum(["proposals", "critiques", "endorsements", "treasury"]).describe("What to browse"),
|
||||
state: z.string().optional().describe("Filter proposals by state: Open, Amended, Adopted, Rejected, Expired, Withdrawn"),
|
||||
@@ -388,7 +388,7 @@ Each batch UTxO holds ~30 AP3X for adoption rewards.`,
|
||||
return {
|
||||
content: [{
|
||||
type: "text",
|
||||
text: `# Governance Proposals (${items.length} found)\n\n${lines.join('\n\n') || 'No proposals match the filters.'}`,
|
||||
text: `# Improvement Proposals (${items.length} found)\n\n${lines.join('\n\n') || 'No proposals match the filters.'}`,
|
||||
}],
|
||||
};
|
||||
} else if (entity === "critiques") {
|
||||
@@ -424,10 +424,10 @@ Each batch UTxO holds ~30 AP3X for adoption rewards.`,
|
||||
return {
|
||||
content: [{
|
||||
type: "text",
|
||||
text: `Failed to browse governance data: ${err.message}
|
||||
text: `Failed to browse proposal data: ${err.message}
|
||||
|
||||
**Troubleshooting Tips:**
|
||||
1. Verify the governance contracts are deployed on testnet
|
||||
1. Verify the module contracts are deployed on this network
|
||||
2. Check that Ogmios is reachable at ${VECTOR_OGMIOS_URL}
|
||||
3. The script addresses may not have any UTxOs yet`,
|
||||
}],
|
||||
@@ -440,10 +440,10 @@ Each batch UTxO holds ~30 AP3X for adoption rewards.`,
|
||||
|
||||
server.tool(
|
||||
"vector_self_improvement_submit_proposal",
|
||||
"Submit a governance proposal to the Vector Self-Improvement Module. Requires staking AP3X. Provide proposalDocument (JSON string) for automatic IPFS upload via Filebase and blake2b_256 hashing, OR provide proposalHash and storageUri manually.",
|
||||
"Submit an improvement proposal to the Vector Self-Improvement Module. Requires staking AP3X. Provide proposalDocument (JSON string) for automatic IPFS upload via Filebase and blake2b_256 hashing, OR provide proposalHash and storageUri manually.",
|
||||
{
|
||||
mnemonic: z.string().describe("15 or 24-word BIP39 mnemonic for the wallet"),
|
||||
agentDid: z.string().describe("Agent DID (hex) — the asset name from Agent Registry NFT"),
|
||||
agentDid: z.string().describe("Agent DID (hex) - the asset name from Agent Registry NFT"),
|
||||
proposalDocument: z.string().optional().describe("Full proposal document as JSON string. Uploaded to IPFS via Filebase; hash and CID computed automatically. If provided, proposalHash and storageUri are ignored."),
|
||||
proposalHash: z.string().optional().describe("blake2b_256 hash of proposal document (64 hex chars). Required if proposalDocument is not provided."),
|
||||
proposalType: z.enum(["ParameterChange", "TreasurySpend", "ProtocolUpgrade", "GameActivation", "GeneralSuggestion"]).describe("Category of the proposal"),
|
||||
@@ -601,7 +601,7 @@ Each batch UTxO holds ~30 AP3X for adoption rewards.`,
|
||||
throw new Error(
|
||||
`Reference script UTxOs missing: found ${refScriptUtxos.length}/2. ` +
|
||||
`spend_ref=${GOV_PROPOSAL_SPEND_REF}, mint_ref=${GOV_PROPOSAL_MINT_REF}. ` +
|
||||
`These UTxOs may have been consumed — redeploy with scripts/redeploy_ref_scripts.py ` +
|
||||
`These UTxOs may have been consumed - redeploy with scripts/redeploy_ref_scripts.py ` +
|
||||
`and update GOV_PROPOSAL_SPEND_REF / GOV_PROPOSAL_MINT_REF env vars.`
|
||||
);
|
||||
}
|
||||
@@ -614,7 +614,7 @@ Each batch UTxO holds ~30 AP3X for adoption rewards.`,
|
||||
);
|
||||
}
|
||||
|
||||
// Get governance infrastructure reference inputs
|
||||
// Get module infrastructure reference inputs
|
||||
const govRefUtxos = await lucid.utxosByOutRef([
|
||||
parseUtxoRef(GOV_PARAMS_UTXO),
|
||||
parseUtxoRef(GOV_ORACLE_UTXO),
|
||||
@@ -708,7 +708,7 @@ activity tracking token (\`pact_\`) minted. Visible on the Foundation dashboard.
|
||||
|
||||
server.tool(
|
||||
"vector_self_improvement_critique",
|
||||
"Submit a critique on a governance proposal. Critiques can support, oppose, or propose amendments. Requires staking AP3X. Provide critiqueDocument (JSON string) for automatic IPFS upload, or critiqueHash + storageUri manually.",
|
||||
"Submit a critique on an improvement proposal. Critiques can support, oppose, or propose amendments. Requires staking AP3X. Provide critiqueDocument (JSON string) for automatic IPFS upload, or critiqueHash + storageUri manually.",
|
||||
{
|
||||
mnemonic: z.string().describe("15 or 24-word BIP39 mnemonic for the wallet"),
|
||||
agentDid: z.string().describe("Agent DID (hex)"),
|
||||
@@ -830,7 +830,7 @@ ${ipfsCid ? `**IPFS CID:** ${ipfsCid}\n**Hash (auto-computed):** ${finalHash}` :
|
||||
|
||||
server.tool(
|
||||
"vector_self_improvement_endorse",
|
||||
"Endorse a governance proposal by staking AP3X. Endorsements signal support to the Foundation Council and are weighted by stake amount.",
|
||||
"Endorse an improvement proposal by staking AP3X. Endorsements signal support to the Foundation Council and are weighted by stake amount.",
|
||||
{
|
||||
mnemonic: z.string().describe("15 or 24-word BIP39 mnemonic for the wallet"),
|
||||
agentDid: z.string().describe("Agent DID (hex)"),
|
||||
@@ -861,7 +861,7 @@ ${ipfsCid ? `**IPFS CID:** ${ipfsCid}\n**Hash (auto-computed):** ${finalHash}` :
|
||||
const tip = await provider.getNetworkTip?.() || { slot: 0 };
|
||||
const currentSlot = tip.slot || 0;
|
||||
|
||||
// Build GovernanceEndorsementDatum
|
||||
// Build the endorsement datum
|
||||
const endorsementDatum = Data.to(new Constr(0, [
|
||||
agentDid, // endorser_did
|
||||
new Constr(0, [vkeyHash]), // endorser_credential
|
||||
@@ -916,7 +916,7 @@ ${ipfsCid ? `**IPFS CID:** ${ipfsCid}\n**Hash (auto-computed):** ${finalHash}` :
|
||||
|
||||
server.tool(
|
||||
"vector_self_improvement_analyze_metrics",
|
||||
"Analyze governance metrics: proposal activity, adoption rate, treasury health, and engagement statistics. Read-only — no mnemonic needed.",
|
||||
"Analyze proposal metrics: proposal activity, adoption rate, treasury health, and engagement statistics. Read-only - no mnemonic needed.",
|
||||
{
|
||||
focus: z.enum(["overview", "adoption", "treasury", "activity"]).default("overview").describe("Analysis focus area"),
|
||||
},
|
||||
@@ -985,7 +985,7 @@ ${Number(treasuryTotal) < 2_500_000_000 ? '**WARNING:** Treasury below 2,500 AP3
|
||||
return {
|
||||
content: [{
|
||||
type: "text",
|
||||
text: `# Governance Metrics${focus !== 'overview' ? ` (${focus})` : ''}
|
||||
text: `# Proposal Metrics${focus !== 'overview' ? ` (${focus})` : ''}
|
||||
|
||||
## Proposals
|
||||
- **Total on-chain:** ${totalCount}
|
||||
@@ -1009,11 +1009,11 @@ ${Number(treasuryTotal) < 2_500_000_000 ? '\n**WARNING:** Treasury below alert t
|
||||
return {
|
||||
content: [{
|
||||
type: "text",
|
||||
text: `Failed to analyze governance metrics: ${err.message}
|
||||
text: `Failed to analyze proposal metrics: ${err.message}
|
||||
|
||||
**Troubleshooting Tips:**
|
||||
1. Verify Ogmios endpoint is reachable
|
||||
2. The governance contracts may not be deployed yet`,
|
||||
2. The module contracts may not be deployed yet`,
|
||||
}],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -39,10 +39,10 @@ if (existsSync(envPath)) {
|
||||
// Configuration from environment variables
|
||||
const VECTOR_OGMIOS_URL = process.env.VECTOR_OGMIOS_URL || 'https://ogmios.vector.testnet.apexfusion.org';
|
||||
const VECTOR_SUBMIT_URL = process.env.VECTOR_SUBMIT_URL || 'https://submit.vector.testnet.apexfusion.org/api/submit/tx';
|
||||
const VECTOR_KOIOS_URL = process.env.VECTOR_KOIOS_URL || 'https://koios.vector.testnet.apexfusion.org/';
|
||||
const VECTOR_KOIOS_URL = process.env.VECTOR_KOIOS_URL || 'https://v2.koios.vector.testnet.apexfusion.org/';
|
||||
const VECTOR_EXPLORER_URL = process.env.VECTOR_EXPLORER_URL || 'https://vector.testnet.apexscan.org';
|
||||
|
||||
// Helper function to format ADA amounts
|
||||
// Helper function to format AP3X amounts
|
||||
function lovelaceToAda(lovelace: string | number | bigint): string {
|
||||
if (lovelace === undefined || lovelace === null) return '0.000000';
|
||||
try {
|
||||
@@ -159,7 +159,7 @@ export async function getWalletInfo(mnemonic: string, accountIndex: number = 0):
|
||||
};
|
||||
}
|
||||
|
||||
// Send ADA transaction
|
||||
// Send AP3X transaction
|
||||
export async function sendAda(
|
||||
recipientAddress: string,
|
||||
amountAda: number,
|
||||
@@ -234,8 +234,8 @@ export async function sendTokens(
|
||||
throw new Error('Amount must be a positive number');
|
||||
}
|
||||
|
||||
// Safety check on ADA portion
|
||||
const adaLovelace = adaAmount ? Math.floor(adaAmount * 1_000_000) : 2_000_000; // min ~2 ADA
|
||||
// Safety check on AP3X portion
|
||||
const adaLovelace = adaAmount ? Math.floor(adaAmount * 1_000_000) : 2_000_000; // min ~2 AP3X
|
||||
const safetyCheck = safetyLayer.checkTransaction(adaLovelace);
|
||||
if (!safetyCheck.allowed) {
|
||||
throw new Error(`Safety limit exceeded: ${safetyCheck.reason}`);
|
||||
@@ -260,7 +260,7 @@ export async function sendTokens(
|
||||
|
||||
const outputLovelace = adaAmount
|
||||
? BigInt(Math.floor(adaAmount * 1_000_000))
|
||||
: BigInt(2_000_000); // Default min ADA
|
||||
: BigInt(2_000_000); // Default min AP3X
|
||||
|
||||
let tx = lucid.newTx()
|
||||
.pay.ToAddress(recipientAddress, {
|
||||
@@ -303,7 +303,7 @@ export async function buildTransaction(
|
||||
throw new Error('At least one output is required');
|
||||
}
|
||||
|
||||
// Calculate total ADA across all outputs for safety check
|
||||
// Calculate total AP3X across all outputs for safety check
|
||||
const totalLovelace = outputs.reduce((sum, o) => sum + o.lovelace, 0);
|
||||
const safetyCheck = safetyLayer.checkTransaction(totalLovelace);
|
||||
if (!safetyCheck.allowed) {
|
||||
@@ -494,7 +494,7 @@ export async function interactWithContract(
|
||||
const signedTx = await completedTx.sign.withWallet().complete();
|
||||
const txHash = await signedTx.submit();
|
||||
|
||||
// No spend recording for collecting — funds are coming back to wallet
|
||||
// No spend recording for collecting - funds are coming back to wallet
|
||||
|
||||
return {
|
||||
txHash,
|
||||
@@ -508,10 +508,10 @@ export async function interactWithContract(
|
||||
// Register all Vector MCP tools
|
||||
export function registerVectorTools(server: McpServer) {
|
||||
|
||||
// vector_get_balance — Get balance for any address
|
||||
// vector_get_balance - Get balance for any address
|
||||
server.tool(
|
||||
"vector_get_balance",
|
||||
"Get ADA and token balances for a Vector address",
|
||||
"Get AP3X and token balances for a Vector address",
|
||||
{
|
||||
address: z.string().describe("Vector address to check (addr1...)"),
|
||||
},
|
||||
@@ -563,7 +563,7 @@ export function registerVectorTools(server: McpServer) {
|
||||
type: "text",
|
||||
text: `Vector Address Balance for ${address}:
|
||||
|
||||
ADA Balance: ${lovelaceToAda(adaBalance)} ADA
|
||||
AP3X Balance: ${lovelaceToAda(adaBalance)} AP3X
|
||||
UTxO Count: ${utxos.length}
|
||||
|
||||
${tokens.length > 0 ? `Token Holdings (${tokens.length}):\n${tokenList}` : 'No token holdings found'}`,
|
||||
@@ -581,7 +581,7 @@ ${tokens.length > 0 ? `Token Holdings (${tokens.length}):\n${tokenList}` : 'No t
|
||||
}
|
||||
);
|
||||
|
||||
// vector_get_address — Get the agent's wallet address and balance
|
||||
// vector_get_address - Get the agent's wallet address and balance
|
||||
server.tool(
|
||||
"vector_get_address",
|
||||
"Get the Vector wallet address, balance, and token holdings derived from a mnemonic",
|
||||
@@ -606,7 +606,7 @@ ${tokens.length > 0 ? `Token Holdings (${tokens.length}):\n${tokenList}` : 'No t
|
||||
text: `# Vector Wallet Information
|
||||
|
||||
Address: ${walletInfo.address}
|
||||
ADA Balance: ${walletInfo.ada} ADA
|
||||
AP3X Balance: ${walletInfo.ada} AP3X
|
||||
UTXO Count: ${walletInfo.utxoCount}
|
||||
|
||||
${walletInfo.tokens.length > 0 ? `## Token Holdings (${walletInfo.tokens.length}):\n${tokenList}` : 'No token holdings found'}`,
|
||||
@@ -629,7 +629,7 @@ ${walletInfo.tokens.length > 0 ? `## Token Holdings (${walletInfo.tokens.length}
|
||||
}
|
||||
);
|
||||
|
||||
// vector_get_utxos — List UTxOs for an address or the wallet
|
||||
// vector_get_utxos - List UTxOs for an address or the wallet
|
||||
server.tool(
|
||||
"vector_get_utxos",
|
||||
"List unspent transaction outputs (UTxOs) for a Vector address or a wallet derived from a mnemonic",
|
||||
@@ -673,7 +673,7 @@ ${walletInfo.tokens.length > 0 ? `## Token Holdings (${walletInfo.tokens.length}
|
||||
const utxoList = utxos.map((utxo, i) => {
|
||||
const ada = utxo.assets['lovelace'] ? lovelaceToAda(utxo.assets['lovelace']) : '0';
|
||||
const tokenCount = Object.keys(utxo.assets).filter(k => k !== 'lovelace').length;
|
||||
return `${i + 1}. ${utxo.txHash}#${utxo.outputIndex} — ${ada} ADA${tokenCount > 0 ? ` + ${tokenCount} token(s)` : ''}`;
|
||||
return `${i + 1}. ${utxo.txHash}#${utxo.outputIndex} - ${ada} AP3X${tokenCount > 0 ? ` + ${tokenCount} token(s)` : ''}`;
|
||||
}).join('\n');
|
||||
|
||||
return {
|
||||
@@ -698,13 +698,13 @@ ${utxoList}`,
|
||||
}
|
||||
);
|
||||
|
||||
// vector_send_apex — Send APEX with safety limits (or craft unsigned TX)
|
||||
// vector_send_apex - Send APEX with safety limits (or craft unsigned TX)
|
||||
server.tool(
|
||||
"vector_send_apex",
|
||||
"Send ADA from a wallet to a recipient address. Set unsigned_only=true to return unsigned CBOR without submitting (transaction-crafter mode).",
|
||||
"Send AP3X from a wallet to a recipient address. Set unsigned_only=true to return unsigned CBOR without submitting (transaction-crafter mode).",
|
||||
{
|
||||
recipientAddress: z.string().describe("Recipient Vector address (addr1...)"),
|
||||
amount: z.number().min(1).describe("Amount of ADA to send"),
|
||||
amount: z.number().min(1).describe("Amount of AP3X to send"),
|
||||
mnemonic: z.string().describe("15 or 24-word BIP39 mnemonic for the sending wallet"),
|
||||
metadata: z.string().optional().describe("Optional transaction metadata in JSON format"),
|
||||
unsigned_only: z.boolean().optional().default(false).describe("If true, return unsigned TX CBOR without signing or submitting"),
|
||||
@@ -732,9 +732,9 @@ ${utxoList}`,
|
||||
type: "text",
|
||||
text: `# Unsigned Transaction (Transaction-Crafter Mode)
|
||||
|
||||
**Amount:** ${amount} ADA (${lovelaceAmount} lovelace)
|
||||
**Amount:** ${amount} AP3X (${lovelaceAmount} lovelace)
|
||||
**To:** ${recipientAddress}
|
||||
**Estimated Fee:** ${lovelaceToAda(fee)} ADA
|
||||
**Estimated Fee:** ${lovelaceToAda(fee)} AP3X
|
||||
|
||||
**Unsigned TX CBOR:**
|
||||
\`\`\`
|
||||
@@ -750,12 +750,12 @@ This transaction has NOT been submitted. Sign and submit it separately.`,
|
||||
return {
|
||||
content: [{
|
||||
type: "text",
|
||||
text: `# ADA Transaction Successful
|
||||
text: `# AP3X Transaction Successful
|
||||
|
||||
Transaction Hash: ${result.txHash}
|
||||
From: ${result.senderAddress}
|
||||
To: ${result.recipientAddress}
|
||||
Amount: ${result.amount} ADA
|
||||
Amount: ${result.amount} AP3X
|
||||
|
||||
[View on Explorer](${result.links.explorer})`,
|
||||
}],
|
||||
@@ -765,7 +765,7 @@ Amount: ${result.amount} ADA
|
||||
return {
|
||||
content: [{
|
||||
type: "text",
|
||||
text: `Failed to send ADA: ${error.message}
|
||||
text: `Failed to send AP3X: ${error.message}
|
||||
|
||||
**Troubleshooting Tips:**
|
||||
1. Check that your wallet has sufficient balance
|
||||
@@ -778,7 +778,7 @@ Amount: ${result.amount} ADA
|
||||
}
|
||||
);
|
||||
|
||||
// vector_send_tokens — Send native tokens with safety limits (or craft unsigned TX)
|
||||
// vector_send_tokens - Send native tokens with safety limits (or craft unsigned TX)
|
||||
server.tool(
|
||||
"vector_send_tokens",
|
||||
"Send Vector native tokens from a wallet to a recipient address. Set unsigned_only=true to return unsigned CBOR without submitting.",
|
||||
@@ -788,7 +788,7 @@ Amount: ${result.amount} ADA
|
||||
assetName: z.string().describe("Asset name (can be empty for policy-only tokens)"),
|
||||
amount: z.string().describe("Amount of tokens to send"),
|
||||
mnemonic: z.string().describe("15 or 24-word BIP39 mnemonic for the sending wallet"),
|
||||
adaAmount: z.number().optional().describe("Optional ADA to include (uses minimum required if not specified)"),
|
||||
adaAmount: z.number().optional().describe("Optional AP3X to include (uses minimum required if not specified)"),
|
||||
unsigned_only: z.boolean().optional().default(false).describe("If true, return unsigned TX CBOR without signing or submitting"),
|
||||
},
|
||||
async ({ recipientAddress, policyId, assetName, amount, mnemonic, adaAmount, unsigned_only }) => {
|
||||
@@ -821,8 +821,8 @@ Amount: ${result.amount} ADA
|
||||
**Token:** ${formatAssetName(assetNameHex) || policyId.substring(0,8) + '...'}
|
||||
**Amount:** ${amount}
|
||||
**To:** ${recipientAddress}
|
||||
**Included ADA:** ${lovelaceToAda(outputLovelace)} ADA
|
||||
**Estimated Fee:** ${lovelaceToAda(fee)} ADA
|
||||
**Included AP3X:** ${lovelaceToAda(outputLovelace)} AP3X
|
||||
**Estimated Fee:** ${lovelaceToAda(fee)} AP3X
|
||||
|
||||
**Unsigned TX CBOR:**
|
||||
\`\`\`
|
||||
@@ -849,7 +849,7 @@ Token Details:
|
||||
- Asset Name: ${result.token.name || '(none)'}
|
||||
- Amount: ${result.token.amount}
|
||||
|
||||
Included ADA: ${result.ada} ADA
|
||||
Included AP3X: ${result.ada} AP3X
|
||||
|
||||
[View on Explorer](${result.links.explorer})`,
|
||||
}],
|
||||
@@ -862,7 +862,7 @@ Included ADA: ${result.ada} ADA
|
||||
text: `Failed to send tokens: ${error.message}
|
||||
|
||||
**Troubleshooting Tips:**
|
||||
1. Check that your wallet has sufficient ADA and token balance
|
||||
1. Check that your wallet has sufficient AP3X and token balance
|
||||
2. Verify the policy ID and asset name are correct
|
||||
3. Verify the recipient address is correct (addr1...)
|
||||
4. Check spend limits with vector_get_spend_limits`,
|
||||
@@ -872,7 +872,7 @@ Included ADA: ${result.ada} ADA
|
||||
}
|
||||
);
|
||||
|
||||
// vector_get_spend_limits — Check safety layer status
|
||||
// vector_get_spend_limits - Check safety layer status
|
||||
server.tool(
|
||||
"vector_get_spend_limits",
|
||||
"Check current spend limits and remaining daily budget",
|
||||
@@ -887,7 +887,7 @@ Included ADA: ${result.ada} ADA
|
||||
const log = safetyLayer.getAuditLog();
|
||||
|
||||
const recentTxs = log.slice(-5).reverse().map((entry) =>
|
||||
`- ${entry.timestamp}: ${lovelaceToAda(entry.amountLovelace)} ADA → ${entry.recipient.substring(0, 20)}... (${entry.txHash.substring(0, 16)}...)`
|
||||
`- ${entry.timestamp}: ${lovelaceToAda(entry.amountLovelace)} AP3X → ${entry.recipient.substring(0, 20)}... (${entry.txHash.substring(0, 16)}...)`
|
||||
).join('\n');
|
||||
|
||||
return {
|
||||
@@ -895,10 +895,10 @@ Included ADA: ${result.ada} ADA
|
||||
type: "text",
|
||||
text: `# Vector Spend Limits
|
||||
|
||||
Per-Transaction Limit: ${lovelaceToAda(status.perTransactionLimit)} ADA
|
||||
Daily Limit: ${lovelaceToAda(status.dailyLimit)} ADA
|
||||
Daily Spent: ${lovelaceToAda(status.dailySpent)} ADA
|
||||
Daily Remaining: ${lovelaceToAda(status.dailyRemaining)} ADA
|
||||
Per-Transaction Limit: ${lovelaceToAda(status.perTransactionLimit)} AP3X
|
||||
Daily Limit: ${lovelaceToAda(status.dailyLimit)} AP3X
|
||||
Daily Spent: ${lovelaceToAda(status.dailySpent)} AP3X
|
||||
Daily Remaining: ${lovelaceToAda(status.dailyRemaining)} AP3X
|
||||
Resets At: ${status.resetTime}
|
||||
|
||||
${log.length > 0 ? `## Recent Transactions (last ${Math.min(5, log.length)}):\n${recentTxs}` : 'No transactions recorded yet.'}`,
|
||||
@@ -916,14 +916,14 @@ ${log.length > 0 ? `## Recent Transactions (last ${Math.min(5, log.length)}):\n$
|
||||
}
|
||||
);
|
||||
|
||||
// vector_build_transaction — Build a complex multi-output transaction
|
||||
// vector_build_transaction - Build a complex multi-output transaction
|
||||
server.tool(
|
||||
"vector_build_transaction",
|
||||
"Build a complex multi-output transaction with metadata. Set submit=true to sign and submit, or false to return unsigned CBOR for review.",
|
||||
{
|
||||
outputs: z.array(z.object({
|
||||
address: z.string().describe("Recipient Vector address"),
|
||||
lovelace: z.number().describe("Amount in lovelace (1 ADA = 1,000,000 lovelace)"),
|
||||
lovelace: z.number().describe("Amount in lovelace (1 AP3X = 1,000,000 lovelace)"),
|
||||
assets: z.record(z.string()).optional().describe("Optional native assets: { 'policyId+assetNameHex': 'quantity' }"),
|
||||
})).min(1).describe("Transaction outputs"),
|
||||
mnemonic: z.string().describe("15 or 24-word BIP39 mnemonic for the signing wallet"),
|
||||
@@ -945,9 +945,9 @@ ${log.length > 0 ? `## Recent Transactions (last ${Math.min(5, log.length)}):\n$
|
||||
text: `# Transaction Submitted
|
||||
|
||||
Transaction Hash: ${result.txHash}
|
||||
Fee: ${result.feeAda} ADA
|
||||
Fee: ${result.feeAda} AP3X
|
||||
Outputs: ${result.outputCount}
|
||||
Total ADA Sent: ${result.totalAda} ADA
|
||||
Total AP3X Sent: ${result.totalAda} AP3X
|
||||
|
||||
[View on Explorer](${result.links?.explorer})`,
|
||||
}],
|
||||
@@ -960,9 +960,9 @@ Total ADA Sent: ${result.totalAda} ADA
|
||||
text: `# Transaction Built (Not Submitted)
|
||||
|
||||
Transaction Hash: ${result.txHash}
|
||||
Fee: ${result.feeAda} ADA
|
||||
Fee: ${result.feeAda} AP3X
|
||||
Outputs: ${result.outputCount}
|
||||
Total ADA: ${result.totalAda} ADA
|
||||
Total AP3X: ${result.totalAda} AP3X
|
||||
|
||||
CBOR (hex): ${result.txCbor.substring(0, 200)}${result.txCbor.length > 200 ? '...' : ''}
|
||||
|
||||
@@ -978,7 +978,7 @@ Use vector_dry_run with this CBOR to simulate, or call vector_build_transaction
|
||||
|
||||
**Troubleshooting Tips:**
|
||||
1. Verify all recipient addresses are valid (addr1...)
|
||||
2. Ensure wallet has enough ADA for outputs + fees
|
||||
2. Ensure wallet has enough AP3X for outputs + fees
|
||||
3. Check spend limits with vector_get_spend_limits`,
|
||||
}],
|
||||
};
|
||||
@@ -986,10 +986,10 @@ Use vector_dry_run with this CBOR to simulate, or call vector_build_transaction
|
||||
}
|
||||
);
|
||||
|
||||
// vector_dry_run — Simulate a transaction without submitting
|
||||
// vector_dry_run - Simulate a transaction without submitting
|
||||
server.tool(
|
||||
"vector_dry_run",
|
||||
"Simulate a transaction without submitting — returns fee estimate and validation result",
|
||||
"Simulate a transaction without submitting - returns fee estimate and validation result",
|
||||
{
|
||||
txCbor: z.string().optional().describe("Hex-encoded CBOR of a built transaction to evaluate"),
|
||||
outputs: z.array(z.object({
|
||||
@@ -1071,7 +1071,7 @@ Use vector_dry_run with this CBOR to simulate, or call vector_build_transaction
|
||||
executionUnits: (totalMemory > 0 || totalCpu > 0) ? { memory: totalMemory, cpu: totalCpu } : undefined,
|
||||
};
|
||||
} catch (evalErr) {
|
||||
// evaluateTransaction failed — still return fee if we built the tx
|
||||
// evaluateTransaction failed - still return fee if we built the tx
|
||||
if (feeFromBuild) {
|
||||
evalResult = {
|
||||
valid: true,
|
||||
@@ -1095,7 +1095,7 @@ Use vector_dry_run with this CBOR to simulate, or call vector_build_transaction
|
||||
text: `# Dry Run Result
|
||||
|
||||
Valid: ${evalResult.valid ? 'Yes' : 'No'}
|
||||
Estimated Fee: ${evalResult.feeAda} ADA (${evalResult.fee} lovelace)
|
||||
Estimated Fee: ${evalResult.feeAda} AP3X (${evalResult.fee} lovelace)
|
||||
${evalResult.executionUnits ? `Execution Units: Memory ${evalResult.executionUnits.memory}, CPU ${evalResult.executionUnits.cpu}` : ''}
|
||||
${evalResult.error ? `\nNote: ${evalResult.error}` : ''}
|
||||
|
||||
@@ -1114,7 +1114,7 @@ No transaction was submitted to the network.`,
|
||||
}
|
||||
);
|
||||
|
||||
// vector_get_transaction_history — Get transaction history via Koios
|
||||
// vector_get_transaction_history - Get transaction history via Koios
|
||||
server.tool(
|
||||
"vector_get_transaction_history",
|
||||
"Get transaction history for a Vector address via Koios indexed queries",
|
||||
@@ -1156,7 +1156,7 @@ No transaction was submitted to the network.`,
|
||||
|
||||
const txList = txs.map((tx, i) => {
|
||||
const feeAda = tx.fee ? lovelaceToAda(tx.fee) : 'N/A';
|
||||
return `${i + 1}. ${tx.txHash}\n Block: ${tx.blockHeight} | Time: ${tx.blockTime} | Fee: ${feeAda} ADA`;
|
||||
return `${i + 1}. ${tx.txHash}\n Block: ${tx.blockHeight} | Time: ${tx.blockTime} | Fee: ${feeAda} AP3X`;
|
||||
}).join('\n\n');
|
||||
|
||||
return {
|
||||
@@ -1188,7 +1188,7 @@ ${txList}
|
||||
}
|
||||
);
|
||||
|
||||
// vector_deploy_contract — Deploy a Plutus/Aiken smart contract
|
||||
// vector_deploy_contract - Deploy a Plutus/Aiken smart contract
|
||||
server.tool(
|
||||
"vector_deploy_contract",
|
||||
"Deploy a Plutus/Aiken smart contract to Vector by sending funds to its script address",
|
||||
@@ -1197,7 +1197,7 @@ ${txList}
|
||||
scriptType: z.enum(["PlutusV1", "PlutusV2", "PlutusV3"]).describe("Script version"),
|
||||
mnemonic: z.string().describe("15 or 24-word BIP39 mnemonic for the deploying wallet"),
|
||||
initialDatum: z.string().optional().describe("Initial datum as CBOR hex. Use 'd87980' for void/unit datum. Defaults to void if omitted."),
|
||||
lovelaceAmount: z.number().optional().describe("ADA to lock at the script address in lovelace (default: 2,000,000 = 2 ADA)"),
|
||||
lovelaceAmount: z.number().optional().describe("AP3X to lock at the script address in lovelace (default: 2,000,000 = 2 AP3X)"),
|
||||
},
|
||||
async ({ scriptCbor, scriptType, mnemonic, initialDatum, lovelaceAmount }) => {
|
||||
const rateCheck = rateLimiter.check();
|
||||
@@ -1237,7 +1237,7 @@ Funds locked at the script address. Use vector_interact_contract to interact wit
|
||||
|
||||
**Troubleshooting Tips:**
|
||||
1. Verify the script CBOR is valid hex (compiled Aiken or Plutus output)
|
||||
2. Ensure wallet has sufficient ADA for the locked amount + fees
|
||||
2. Ensure wallet has sufficient AP3X for the locked amount + fees
|
||||
3. Check that the script type matches the compiled version (PlutusV1/V2/V3)
|
||||
4. Check spend limits with vector_get_spend_limits`,
|
||||
}],
|
||||
@@ -1246,10 +1246,10 @@ Funds locked at the script address. Use vector_interact_contract to interact wit
|
||||
}
|
||||
);
|
||||
|
||||
// vector_interact_contract — Interact with a deployed smart contract
|
||||
// vector_interact_contract - Interact with a deployed smart contract
|
||||
server.tool(
|
||||
"vector_interact_contract",
|
||||
"Interact with a deployed Plutus/Aiken smart contract — lock funds or spend from it",
|
||||
"Interact with a deployed Plutus/Aiken smart contract - lock AP3X or spend from it",
|
||||
{
|
||||
scriptCbor: z.string().describe("Compiled Plutus/Aiken script in CBOR hex"),
|
||||
scriptType: z.enum(["PlutusV1", "PlutusV2", "PlutusV3"]).describe("Script version"),
|
||||
@@ -1257,7 +1257,7 @@ Funds locked at the script address. Use vector_interact_contract to interact wit
|
||||
mnemonic: z.string().describe("15 or 24-word BIP39 mnemonic for the wallet"),
|
||||
redeemer: z.string().optional().describe("Redeemer as CBOR hex (required for spend, use 'd87980' for void)"),
|
||||
datum: z.string().optional().describe("Datum as CBOR hex (required for lock, use 'd87980' for void)"),
|
||||
lovelaceAmount: z.number().optional().describe("Lovelace to lock (for lock action, default: 2,000,000 = 2 ADA)"),
|
||||
lovelaceAmount: z.number().optional().describe("Lovelace to lock (for lock action, default: 2,000,000 = 2 AP3X)"),
|
||||
utxoRef: z.object({
|
||||
txHash: z.string(),
|
||||
outputIndex: z.number(),
|
||||
@@ -1307,8 +1307,8 @@ Funds ${actionVerb} the script address.
|
||||
|
||||
**Troubleshooting Tips:**
|
||||
1. For 'spend': ensure the script address has UTxOs and the redeemer satisfies the validator
|
||||
2. For 'lock': ensure wallet has sufficient ADA and the datum matches the script's expectations
|
||||
3. Spending requires collateral — ensure wallet has a pure-ADA UTxO (no native tokens)
|
||||
2. For 'lock': ensure wallet has sufficient AP3X and the datum matches the script's expectations
|
||||
3. Spending requires collateral - ensure wallet has a pure-AP3X UTxO (no native tokens)
|
||||
4. Verify the script CBOR matches the deployed script exactly
|
||||
5. Check spend limits with vector_get_spend_limits`,
|
||||
}],
|
||||
|
||||
Reference in New Issue
Block a user