feat(builder)!: replace custodial signing tools with keyless build_* tools (wallet/tx family)
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
import { z } from "zod/v4";
|
||||
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
||||
import { Lucid, fromText, Data, applyDoubleCborEncoding, validatorToAddress, validatorToScriptHash, getAddressDetails } from '@lucid-evolution/lucid';
|
||||
import type { SpendingValidator } from '@lucid-evolution/lucid';
|
||||
import { Lucid } from '@lucid-evolution/lucid';
|
||||
|
||||
import * as dotenv from 'dotenv';
|
||||
import { resolve } from 'path';
|
||||
@@ -18,10 +17,12 @@ import { pollUntilConfirmed, buildConfirmationCheck } from './poll.js';
|
||||
import { registerAgentNetworkTools } from './agent-network.js';
|
||||
import { registerSelfImprovementTools } from './self-improvement.js';
|
||||
import type {
|
||||
VectorToken, VectorWalletInfo, VectorAdaTransactionResult, VectorTokenTransactionResult,
|
||||
TxOutput, VectorBuildTransactionResult, VectorDryRunResult, VectorDeployContractResult,
|
||||
VectorInteractContractResult,
|
||||
VectorToken, VectorWalletInfo, VectorDryRunResult,
|
||||
} from '@apexfusion/vector-mcp-shared/types';
|
||||
import {
|
||||
lucidForAddress, buildSendApex, buildSendTokens, buildMultiOutput,
|
||||
buildDeployContract, buildInteractContract,
|
||||
} from './build.js';
|
||||
|
||||
// Direct .env loading
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
@@ -127,350 +128,13 @@ export async function getWalletInfo(mnemonic: string, accountIndex: number = 0):
|
||||
};
|
||||
}
|
||||
|
||||
// Send AP3X transaction
|
||||
export async function sendAda(
|
||||
recipientAddress: string,
|
||||
amountAda: number,
|
||||
mnemonic: string,
|
||||
metadata: any = null
|
||||
): Promise<VectorAdaTransactionResult> {
|
||||
if (!recipientAddress) {
|
||||
throw new Error('Recipient address is required');
|
||||
}
|
||||
|
||||
if (typeof amountAda !== 'number' || amountAda <= 0) {
|
||||
throw new Error('Amount must be a positive number');
|
||||
}
|
||||
|
||||
const lovelaceAmount = Math.floor(amountAda * 1_000_000);
|
||||
|
||||
// Safety check
|
||||
const safetyCheck = safetyLayer.checkTransaction(lovelaceAmount);
|
||||
if (!safetyCheck.allowed) {
|
||||
throw new Error(`Safety limit exceeded: ${safetyCheck.reason}`);
|
||||
}
|
||||
|
||||
const lucid = await initLucid(mnemonic);
|
||||
const senderAddress = await lucid.wallet().address();
|
||||
|
||||
// Validate recipient address
|
||||
try {
|
||||
getAddressDetails(recipientAddress);
|
||||
} catch (error) {
|
||||
throw new Error(`Invalid recipient address: ${error instanceof Error ? error.message : String(error)}`);
|
||||
}
|
||||
|
||||
// Build transaction
|
||||
let tx = lucid.newTx()
|
||||
.pay.ToAddress(recipientAddress, { lovelace: BigInt(lovelaceAmount) });
|
||||
|
||||
if (metadata) {
|
||||
const parsedMetadata = typeof metadata === 'string' ? JSON.parse(metadata) : metadata;
|
||||
tx = tx.attachMetadata(674, parsedMetadata);
|
||||
}
|
||||
|
||||
const signBuilder = await tx.complete();
|
||||
const signedTx = await signBuilder.sign.withWallet().complete();
|
||||
const txHash = await signedTx.submit();
|
||||
|
||||
// Record in safety layer
|
||||
safetyLayer.recordTransaction(txHash, lovelaceAmount, recipientAddress);
|
||||
|
||||
return {
|
||||
txHash,
|
||||
senderAddress,
|
||||
recipientAddress,
|
||||
amount: amountAda,
|
||||
links: {
|
||||
explorer: explorerTxLink(txHash),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// Send tokens transaction
|
||||
export async function sendTokens(
|
||||
recipientAddress: string,
|
||||
policyId: string,
|
||||
assetName: string,
|
||||
amount: string,
|
||||
mnemonic: string,
|
||||
adaAmount: number | null = null
|
||||
): Promise<VectorTokenTransactionResult> {
|
||||
if (!recipientAddress) throw new Error('Recipient address is required');
|
||||
if (!policyId) throw new Error('Policy ID is required');
|
||||
if (!amount || isNaN(Number(amount)) || Number(amount) <= 0) {
|
||||
throw new Error('Amount must be a positive number');
|
||||
}
|
||||
|
||||
// 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}`);
|
||||
}
|
||||
|
||||
const lucid = await initLucid(mnemonic);
|
||||
const senderAddress = await lucid.wallet().address();
|
||||
|
||||
try {
|
||||
getAddressDetails(recipientAddress);
|
||||
} catch (error) {
|
||||
throw new Error(`Invalid recipient address: ${error instanceof Error ? error.message : String(error)}`);
|
||||
}
|
||||
|
||||
let assetNameHex = assetName;
|
||||
if (assetName && !/^[0-9a-fA-F]+$/.test(assetName)) {
|
||||
assetNameHex = fromText(assetName);
|
||||
}
|
||||
|
||||
const unit = `${policyId}${assetNameHex}`;
|
||||
const assets = { [unit]: BigInt(amount) };
|
||||
|
||||
const outputLovelace = adaAmount
|
||||
? BigInt(Math.floor(adaAmount * 1_000_000))
|
||||
: BigInt(2_000_000); // Default min AP3X
|
||||
|
||||
let tx = lucid.newTx()
|
||||
.pay.ToAddress(recipientAddress, {
|
||||
lovelace: outputLovelace,
|
||||
...assets,
|
||||
});
|
||||
|
||||
const signBuilder = await tx.complete();
|
||||
const signedTx = await signBuilder.sign.withWallet().complete();
|
||||
const txHash = await signedTx.submit();
|
||||
|
||||
safetyLayer.recordTransaction(txHash, Number(outputLovelace), recipientAddress);
|
||||
|
||||
const displayAssetName = assetName ? formatAssetName(assetNameHex) : '';
|
||||
|
||||
return {
|
||||
txHash,
|
||||
senderAddress,
|
||||
recipientAddress,
|
||||
token: {
|
||||
policyId,
|
||||
name: displayAssetName,
|
||||
amount,
|
||||
},
|
||||
ada: lovelaceToAda(outputLovelace),
|
||||
links: {
|
||||
explorer: explorerTxLink(txHash),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// Build a complex multi-output transaction
|
||||
export async function buildTransaction(
|
||||
outputs: TxOutput[],
|
||||
mnemonic: string,
|
||||
metadata: any = null,
|
||||
submit: boolean = false,
|
||||
): Promise<VectorBuildTransactionResult> {
|
||||
if (!outputs || outputs.length === 0) {
|
||||
throw new Error('At least one output is required');
|
||||
}
|
||||
|
||||
// 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) {
|
||||
throw new Error(`Safety limit exceeded: ${safetyCheck.reason}`);
|
||||
}
|
||||
|
||||
const lucid = await initLucid(mnemonic);
|
||||
|
||||
let tx = lucid.newTx();
|
||||
|
||||
for (const output of outputs) {
|
||||
const assets: Record<string, bigint> = {
|
||||
lovelace: BigInt(output.lovelace),
|
||||
};
|
||||
if (output.assets) {
|
||||
for (const [unit, qty] of Object.entries(output.assets)) {
|
||||
assets[unit] = BigInt(qty);
|
||||
}
|
||||
}
|
||||
tx = tx.pay.ToAddress(output.address, assets);
|
||||
}
|
||||
|
||||
if (metadata) {
|
||||
const parsedMetadata = typeof metadata === 'string' ? JSON.parse(metadata) : metadata;
|
||||
tx = tx.attachMetadata(674, parsedMetadata);
|
||||
}
|
||||
|
||||
const completedTx = await tx.complete();
|
||||
const txJson = (completedTx as any).toJSON?.() ?? {};
|
||||
const fee = txJson?.body?.fee ?? '0';
|
||||
const txHash = completedTx.toHash();
|
||||
const txCbor = completedTx.toCBOR();
|
||||
|
||||
if (submit) {
|
||||
const signedTx = await completedTx.sign.withWallet().complete();
|
||||
const submittedHash = await signedTx.submit();
|
||||
|
||||
safetyLayer.recordTransaction(submittedHash, totalLovelace, outputs.map(o => o.address).join(', '));
|
||||
|
||||
return {
|
||||
txCbor: '',
|
||||
txHash: submittedHash,
|
||||
fee: String(fee),
|
||||
feeAda: lovelaceToAda(fee),
|
||||
outputCount: outputs.length,
|
||||
totalAda: lovelaceToAda(totalLovelace),
|
||||
submitted: true,
|
||||
links: { explorer: explorerTxLink(submittedHash) },
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
txCbor,
|
||||
txHash,
|
||||
fee: String(fee),
|
||||
feeAda: lovelaceToAda(fee),
|
||||
outputCount: outputs.length,
|
||||
totalAda: lovelaceToAda(totalLovelace),
|
||||
submitted: false,
|
||||
};
|
||||
}
|
||||
|
||||
// Deploy a smart contract by locking funds at the script address
|
||||
export async function deployContract(
|
||||
scriptCbor: string,
|
||||
scriptType: string,
|
||||
mnemonic: string,
|
||||
initialDatum: string | null = null,
|
||||
lovelaceAmount: number = 2_000_000,
|
||||
): Promise<VectorDeployContractResult> {
|
||||
const safetyCheck = safetyLayer.checkTransaction(lovelaceAmount);
|
||||
if (!safetyCheck.allowed) {
|
||||
throw new Error(`Safety limit exceeded: ${safetyCheck.reason}`);
|
||||
}
|
||||
|
||||
const lucid = await initLucid(mnemonic);
|
||||
|
||||
const validator: SpendingValidator = {
|
||||
type: scriptType as any,
|
||||
script: applyDoubleCborEncoding(scriptCbor),
|
||||
};
|
||||
|
||||
const scriptAddress = validatorToAddress('Mainnet', validator);
|
||||
const scriptHash = validatorToScriptHash(validator);
|
||||
|
||||
const datum = initialDatum || Data.void();
|
||||
|
||||
let tx = lucid.newTx()
|
||||
.pay.ToAddressWithData(scriptAddress, { kind: "inline", value: datum }, { lovelace: BigInt(lovelaceAmount) });
|
||||
|
||||
const signBuilder = await tx.complete();
|
||||
const signedTx = await signBuilder.sign.withWallet().complete();
|
||||
const txHash = await signedTx.submit();
|
||||
|
||||
safetyLayer.recordTransaction(txHash, lovelaceAmount, scriptAddress);
|
||||
|
||||
return {
|
||||
txHash,
|
||||
scriptAddress,
|
||||
scriptHash,
|
||||
scriptType,
|
||||
links: { explorer: explorerTxLink(txHash) },
|
||||
};
|
||||
}
|
||||
|
||||
// Interact with a deployed smart contract (lock or spend)
|
||||
export async function interactWithContract(
|
||||
scriptCbor: string,
|
||||
scriptType: string,
|
||||
action: 'spend' | 'lock',
|
||||
mnemonic: string,
|
||||
redeemer: string | null = null,
|
||||
datum: string | null = null,
|
||||
lovelaceAmount: number = 2_000_000,
|
||||
utxoRef: { txHash: string; outputIndex: number } | null = null,
|
||||
assets: Record<string, string> | null = null,
|
||||
): Promise<VectorInteractContractResult> {
|
||||
const lucid = await initLucid(mnemonic);
|
||||
const walletAddress = await lucid.wallet().address();
|
||||
|
||||
const validator: SpendingValidator = {
|
||||
type: scriptType as any,
|
||||
script: applyDoubleCborEncoding(scriptCbor),
|
||||
};
|
||||
|
||||
const scriptAddress = validatorToAddress('Mainnet', validator);
|
||||
|
||||
if (action === 'lock') {
|
||||
const safetyCheck = safetyLayer.checkTransaction(lovelaceAmount);
|
||||
if (!safetyCheck.allowed) {
|
||||
throw new Error(`Safety limit exceeded: ${safetyCheck.reason}`);
|
||||
}
|
||||
|
||||
const datumData = datum || Data.void();
|
||||
const outputAssets: Record<string, bigint> = { lovelace: BigInt(lovelaceAmount) };
|
||||
if (assets) {
|
||||
for (const [unit, qty] of Object.entries(assets)) {
|
||||
outputAssets[unit] = BigInt(qty);
|
||||
}
|
||||
}
|
||||
|
||||
let tx = lucid.newTx()
|
||||
.pay.ToAddressWithData(scriptAddress, { kind: "inline", value: datumData }, outputAssets);
|
||||
|
||||
const signBuilder = await tx.complete();
|
||||
const signedTx = await signBuilder.sign.withWallet().complete();
|
||||
const txHash = await signedTx.submit();
|
||||
|
||||
safetyLayer.recordTransaction(txHash, lovelaceAmount, scriptAddress);
|
||||
|
||||
return {
|
||||
txHash,
|
||||
scriptAddress,
|
||||
action: 'lock',
|
||||
links: { explorer: explorerTxLink(txHash) },
|
||||
};
|
||||
} else {
|
||||
// SPEND: collect from script
|
||||
let scriptUtxos;
|
||||
if (utxoRef) {
|
||||
scriptUtxos = await lucid.utxosByOutRef([utxoRef]);
|
||||
} else {
|
||||
scriptUtxos = await lucid.utxosAt(scriptAddress);
|
||||
}
|
||||
|
||||
if (!scriptUtxos || scriptUtxos.length === 0) {
|
||||
throw new Error(`No UTxOs found at script address ${scriptAddress}`);
|
||||
}
|
||||
|
||||
const redeemerData = redeemer || Data.void();
|
||||
|
||||
let completedTx;
|
||||
try {
|
||||
completedTx = await lucid.newTx()
|
||||
.collectFrom(scriptUtxos, redeemerData)
|
||||
.attach.SpendingValidator(validator)
|
||||
.addSigner(walletAddress)
|
||||
.complete();
|
||||
} catch (err) {
|
||||
// Retry without native UPLC evaluator if it fails
|
||||
completedTx = await lucid.newTx()
|
||||
.collectFrom(scriptUtxos, redeemerData)
|
||||
.attach.SpendingValidator(validator)
|
||||
.addSigner(walletAddress)
|
||||
.complete({ localUPLCEval: false });
|
||||
}
|
||||
|
||||
const signedTx = await completedTx.sign.withWallet().complete();
|
||||
const txHash = await signedTx.submit();
|
||||
|
||||
// No spend recording for collecting - funds are coming back to wallet
|
||||
|
||||
return {
|
||||
txHash,
|
||||
scriptAddress,
|
||||
action: 'spend',
|
||||
links: { explorer: explorerTxLink(txHash) },
|
||||
};
|
||||
}
|
||||
// Shared provider for the keyless build_* tools (Task 5 reuses this).
|
||||
function makeProvider(): OgmiosProvider {
|
||||
return new OgmiosProvider({
|
||||
ogmiosUrl: VECTOR_OGMIOS_URL,
|
||||
submitUrl: VECTOR_SUBMIT_URL,
|
||||
koiosUrl: VECTOR_KOIOS_URL,
|
||||
});
|
||||
}
|
||||
|
||||
// Register all Vector MCP tools
|
||||
@@ -667,66 +331,40 @@ ${utxoList}`,
|
||||
}
|
||||
);
|
||||
|
||||
// vector_send_apex - Send APEX with safety limits (or craft unsigned TX)
|
||||
// vector_build_send_apex - build an UNSIGNED AP3X payment (keyless)
|
||||
server.tool(
|
||||
"vector_send_apex",
|
||||
"Send AP3X from a wallet to a recipient address. Set unsigned_only=true to return unsigned CBOR without submitting (transaction-crafter mode).",
|
||||
"vector_build_send_apex",
|
||||
"Build an UNSIGNED transaction sending AP3X to a recipient. Takes no key material - sign the returned CBOR with your local signer (vector_signer_sign), then broadcast with vector_submit_transaction.",
|
||||
{
|
||||
changeAddress: z.string().describe("Your wallet address (source of funds; receives change). Get it from your local signer's vector_signer_get_address."),
|
||||
recipientAddress: z.string().describe("Recipient Vector address (addr1...)"),
|
||||
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"),
|
||||
},
|
||||
async ({ recipientAddress, amount, mnemonic, metadata, unsigned_only }) => {
|
||||
async ({ changeAddress, recipientAddress, amount, metadata }) => {
|
||||
const rateCheck = rateLimiter.check();
|
||||
if (!rateCheck.allowed) {
|
||||
return { content: [{ type: "text", text: `Rate limit exceeded. Retry after ${rateCheck.retryAfterMs}ms.` }] };
|
||||
}
|
||||
try {
|
||||
if (unsigned_only) {
|
||||
const lovelaceAmount = BigInt(Math.floor(amount * 1_000_000));
|
||||
const lucid = await initLucid(mnemonic);
|
||||
let txBuilder = lucid.newTx().pay.ToAddress(recipientAddress, { lovelace: lovelaceAmount });
|
||||
if (metadata) {
|
||||
const parsedMetadata = typeof metadata === 'string' ? JSON.parse(metadata) : metadata;
|
||||
txBuilder = txBuilder.attachMetadata(674, parsedMetadata);
|
||||
}
|
||||
const tx = await txBuilder.complete();
|
||||
const txJson = (tx as any).toJSON?.() ?? {};
|
||||
const fee = txJson?.body?.fee ?? '0';
|
||||
const txCbor = tx.toCBOR();
|
||||
return {
|
||||
content: [{
|
||||
type: "text",
|
||||
text: `# Unsigned Transaction (Transaction-Crafter Mode)
|
||||
|
||||
**Amount:** ${amount} AP3X (${lovelaceAmount} lovelace)
|
||||
**To:** ${recipientAddress}
|
||||
**Estimated Fee:** ${lovelaceToAda(fee)} AP3X
|
||||
|
||||
**Unsigned TX CBOR:**
|
||||
\`\`\`
|
||||
${txCbor}
|
||||
\`\`\`
|
||||
|
||||
This transaction has NOT been submitted. Sign and submit it separately.`,
|
||||
}],
|
||||
};
|
||||
}
|
||||
|
||||
const result = await sendAda(recipientAddress, amount, mnemonic, metadata);
|
||||
const lucid = await lucidForAddress(makeProvider(), changeAddress);
|
||||
const result = await buildSendApex(lucid, { recipientAddress, amountApex: amount, metadataJson: metadata });
|
||||
return {
|
||||
content: [{
|
||||
type: "text",
|
||||
text: `# AP3X Transaction Successful
|
||||
text: `# Unsigned Transaction Built
|
||||
|
||||
Transaction Hash: ${result.txHash}
|
||||
From: ${result.senderAddress}
|
||||
To: ${result.recipientAddress}
|
||||
Amount: ${result.amount} AP3X
|
||||
**Amount:** ${amount} AP3X
|
||||
**To:** ${recipientAddress}
|
||||
**Estimated Fee:** ${result.feeAda} AP3X
|
||||
**Tx Hash:** ${result.txHash}
|
||||
|
||||
[View on Explorer](${result.links.explorer})`,
|
||||
**Unsigned TX CBOR:**
|
||||
\`\`\`
|
||||
${result.txCbor}
|
||||
\`\`\`
|
||||
|
||||
Non-custodial flow: 1) sign locally with vector_signer_sign { txCbor } — your keys never leave your machine; 2) broadcast with vector_submit_transaction { signedTxCbor }; 3) confirm with vector_await_transaction { txHash }.`,
|
||||
}],
|
||||
};
|
||||
} catch (err) {
|
||||
@@ -734,93 +372,56 @@ Amount: ${result.amount} AP3X
|
||||
return {
|
||||
content: [{
|
||||
type: "text",
|
||||
text: `Failed to send AP3X: ${error.message}
|
||||
text: `Failed to build transaction: ${error.message}
|
||||
|
||||
**Troubleshooting Tips:**
|
||||
1. Check that your wallet has sufficient balance
|
||||
1. Verify the change address is your funded wallet address (addr1...)
|
||||
2. Verify the recipient address is correct (addr1...)
|
||||
3. Check spend limits with vector_get_spend_limits
|
||||
4. Verify the Ogmios endpoint is reachable`,
|
||||
3. Ensure the wallet has enough AP3X for the amount plus fees`,
|
||||
}],
|
||||
};
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// vector_send_tokens - Send native tokens with safety limits (or craft unsigned TX)
|
||||
// vector_build_send_tokens - build an UNSIGNED native-token transfer (keyless)
|
||||
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.",
|
||||
"vector_build_send_tokens",
|
||||
"Build an UNSIGNED transaction sending Vector native tokens. Takes no key material - sign the returned CBOR with your local signer (vector_signer_sign), then broadcast with vector_submit_transaction.",
|
||||
{
|
||||
changeAddress: z.string().describe("Your wallet address (source of funds; receives change). Get it from your local signer's vector_signer_get_address."),
|
||||
recipientAddress: z.string().describe("Recipient Vector address (addr1...)"),
|
||||
policyId: z.string().describe("Token policy ID"),
|
||||
assetName: z.string().describe("Asset name (can be empty for policy-only tokens)"),
|
||||
policyId: z.string().describe("Token policy ID (56 hex characters)"),
|
||||
assetName: z.string().describe("Asset name (text or hex; 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 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"),
|
||||
adaAmount: z.number().optional().describe("Optional AP3X to include with the tokens (defaults to the 2 AP3X minimum)"),
|
||||
},
|
||||
async ({ recipientAddress, policyId, assetName, amount, mnemonic, adaAmount, unsigned_only }) => {
|
||||
async ({ changeAddress, recipientAddress, policyId, assetName, amount, adaAmount }) => {
|
||||
const rateCheck = rateLimiter.check();
|
||||
if (!rateCheck.allowed) {
|
||||
return { content: [{ type: "text", text: `Rate limit exceeded. Retry after ${rateCheck.retryAfterMs}ms.` }] };
|
||||
}
|
||||
try {
|
||||
if (unsigned_only) {
|
||||
const lucid = await initLucid(mnemonic);
|
||||
let assetNameHex = assetName;
|
||||
if (assetName && !/^[0-9a-fA-F]+$/.test(assetName)) {
|
||||
assetNameHex = fromText(assetName);
|
||||
}
|
||||
const unit = `${policyId}${assetNameHex}`;
|
||||
const outputLovelace = adaAmount
|
||||
? BigInt(Math.floor(adaAmount * 1_000_000))
|
||||
: BigInt(2_000_000);
|
||||
const tx = await lucid.newTx()
|
||||
.pay.ToAddress(recipientAddress, { lovelace: outputLovelace, [unit]: BigInt(amount) })
|
||||
.complete();
|
||||
const txJson = (tx as any).toJSON?.() ?? {};
|
||||
const fee = txJson?.body?.fee ?? '0';
|
||||
const txCbor = tx.toCBOR();
|
||||
return {
|
||||
content: [{
|
||||
type: "text",
|
||||
text: `# Unsigned Token Transaction (Transaction-Crafter Mode)
|
||||
|
||||
**Token:** ${formatAssetName(assetNameHex) || policyId.substring(0,8) + '...'}
|
||||
**Amount:** ${amount}
|
||||
**To:** ${recipientAddress}
|
||||
**Included AP3X:** ${lovelaceToAda(outputLovelace)} AP3X
|
||||
**Estimated Fee:** ${lovelaceToAda(fee)} AP3X
|
||||
|
||||
**Unsigned TX CBOR:**
|
||||
\`\`\`
|
||||
${txCbor}
|
||||
\`\`\`
|
||||
|
||||
This transaction has NOT been submitted. Sign and submit it separately.`,
|
||||
}],
|
||||
};
|
||||
}
|
||||
|
||||
const result = await sendTokens(recipientAddress, policyId, assetName, amount, mnemonic, adaAmount);
|
||||
const lucid = await lucidForAddress(makeProvider(), changeAddress);
|
||||
const result = await buildSendTokens(lucid, { recipientAddress, policyId, assetName, amount, apexAmount: adaAmount });
|
||||
const displayName = assetName && !/^[0-9a-fA-F]+$/.test(assetName) ? assetName : (formatAssetName(assetName) || `${policyId.substring(0, 8)}...`);
|
||||
return {
|
||||
content: [{
|
||||
type: "text",
|
||||
text: `# Token Transaction Successful
|
||||
text: `# Unsigned Token Transaction Built
|
||||
|
||||
Transaction Hash: ${result.txHash}
|
||||
From: ${result.senderAddress}
|
||||
To: ${result.recipientAddress}
|
||||
**Token:** ${displayName}
|
||||
**Amount:** ${amount}
|
||||
**To:** ${recipientAddress}
|
||||
**Estimated Fee:** ${result.feeAda} AP3X
|
||||
**Tx Hash:** ${result.txHash}
|
||||
|
||||
Token Details:
|
||||
- Policy ID: ${result.token.policyId}
|
||||
- Asset Name: ${result.token.name || '(none)'}
|
||||
- Amount: ${result.token.amount}
|
||||
**Unsigned TX CBOR:**
|
||||
\`\`\`
|
||||
${result.txCbor}
|
||||
\`\`\`
|
||||
|
||||
Included AP3X: ${result.ada} AP3X
|
||||
|
||||
[View on Explorer](${result.links.explorer})`,
|
||||
Non-custodial flow: 1) sign locally with vector_signer_sign { txCbor }; 2) broadcast with vector_submit_transaction { signedTxCbor }; 3) confirm with vector_await_transaction { txHash }.`,
|
||||
}],
|
||||
};
|
||||
} catch (err) {
|
||||
@@ -828,13 +429,12 @@ Included AP3X: ${result.ada} AP3X
|
||||
return {
|
||||
content: [{
|
||||
type: "text",
|
||||
text: `Failed to send tokens: ${error.message}
|
||||
text: `Failed to build token transaction: ${error.message}
|
||||
|
||||
**Troubleshooting Tips:**
|
||||
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`,
|
||||
1. Check that the wallet at changeAddress holds the token and enough AP3X
|
||||
2. Verify the policy ID (56 hex chars) and asset name are correct
|
||||
3. Verify the recipient address is correct (addr1...)`,
|
||||
}],
|
||||
};
|
||||
}
|
||||
@@ -885,57 +485,44 @@ ${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 an UNSIGNED multi-output transaction (keyless)
|
||||
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.",
|
||||
"Build an UNSIGNED multi-output transaction with optional metadata. Takes no key material and never submits - sign the returned CBOR with your local signer (vector_signer_sign), then broadcast with vector_submit_transaction.",
|
||||
{
|
||||
changeAddress: z.string().describe("Your wallet address (source of funds; receives change). Get it from your local signer's vector_signer_get_address."),
|
||||
outputs: z.array(z.object({
|
||||
address: z.string().describe("Recipient Vector address"),
|
||||
lovelace: z.number().describe("Amount in lovelace (1 AP3X = 1,000,000 lovelace)"),
|
||||
assets: z.record(z.string(), 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"),
|
||||
metadata: z.string().optional().describe("Optional JSON metadata (attached under label 674)"),
|
||||
submit: z.boolean().optional().describe("If true, sign and submit the transaction. If false/omitted, return unsigned CBOR for review."),
|
||||
},
|
||||
async ({ outputs, mnemonic, metadata, submit }) => {
|
||||
async ({ changeAddress, outputs, metadata }) => {
|
||||
const rateCheck = rateLimiter.check();
|
||||
if (!rateCheck.allowed) {
|
||||
return { content: [{ type: "text", text: `Rate limit exceeded. Retry after ${rateCheck.retryAfterMs}ms.` }] };
|
||||
}
|
||||
try {
|
||||
const result = await buildTransaction(outputs, mnemonic, metadata, submit);
|
||||
|
||||
if (result.submitted) {
|
||||
return {
|
||||
content: [{
|
||||
type: "text",
|
||||
text: `# Transaction Submitted
|
||||
|
||||
Transaction Hash: ${result.txHash}
|
||||
Fee: ${result.feeAda} AP3X
|
||||
Outputs: ${result.outputCount}
|
||||
Total AP3X Sent: ${result.totalAda} AP3X
|
||||
|
||||
[View on Explorer](${result.links?.explorer})`,
|
||||
}],
|
||||
};
|
||||
}
|
||||
|
||||
const lucid = await lucidForAddress(makeProvider(), changeAddress);
|
||||
const result = await buildMultiOutput(lucid, { outputs, metadataJson: metadata });
|
||||
const totalLovelace = outputs.reduce((sum, o) => sum + o.lovelace, 0);
|
||||
return {
|
||||
content: [{
|
||||
type: "text",
|
||||
text: `# Transaction Built (Not Submitted)
|
||||
text: `# Unsigned Transaction Built (Not Submitted)
|
||||
|
||||
Transaction Hash: ${result.txHash}
|
||||
Fee: ${result.feeAda} AP3X
|
||||
Outputs: ${result.outputCount}
|
||||
Total AP3X: ${result.totalAda} AP3X
|
||||
**Tx Hash:** ${result.txHash}
|
||||
**Fee:** ${result.feeAda} AP3X
|
||||
**Outputs:** ${outputs.length}
|
||||
**Total AP3X:** ${lovelaceToAda(totalLovelace)} AP3X
|
||||
|
||||
CBOR (hex): ${result.txCbor.substring(0, 200)}${result.txCbor.length > 200 ? '...' : ''}
|
||||
**Unsigned TX CBOR:**
|
||||
\`\`\`
|
||||
${result.txCbor}
|
||||
\`\`\`
|
||||
|
||||
Use vector_dry_run with this CBOR to simulate, or call vector_build_transaction again with submit=true to submit.`,
|
||||
Use vector_dry_run with this CBOR to simulate it, sign it locally with vector_signer_sign, then broadcast with vector_submit_transaction.`,
|
||||
}],
|
||||
};
|
||||
} catch (err) {
|
||||
@@ -947,8 +534,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 AP3X for outputs + fees
|
||||
3. Check spend limits with vector_get_spend_limits`,
|
||||
2. Ensure the wallet at changeAddress has enough AP3X for outputs + fees`,
|
||||
}],
|
||||
};
|
||||
}
|
||||
@@ -1251,44 +837,42 @@ ${txList}
|
||||
}
|
||||
);
|
||||
|
||||
// vector_deploy_contract - Deploy a Plutus/Aiken smart contract
|
||||
// vector_build_deploy_contract - build an UNSIGNED contract deployment (keyless)
|
||||
server.tool(
|
||||
"vector_deploy_contract",
|
||||
"Deploy a Plutus/Aiken smart contract to Vector by sending funds to its script address",
|
||||
"vector_build_deploy_contract",
|
||||
"Build an UNSIGNED transaction deploying a Plutus/Aiken smart contract (locks funds at its script address). Takes no key material - sign with your local signer, then broadcast with vector_submit_transaction.",
|
||||
{
|
||||
changeAddress: z.string().describe("Your wallet address (source of funds; receives change). Get it from your local signer's vector_signer_get_address."),
|
||||
scriptCbor: z.string().describe("Compiled Plutus/Aiken script in CBOR hex format"),
|
||||
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("AP3X to lock at the script address in lovelace (default: 2,000,000 = 2 AP3X)"),
|
||||
},
|
||||
async ({ scriptCbor, scriptType, mnemonic, initialDatum, lovelaceAmount }) => {
|
||||
async ({ changeAddress, scriptCbor, scriptType, initialDatum, lovelaceAmount }) => {
|
||||
const rateCheck = rateLimiter.check();
|
||||
if (!rateCheck.allowed) {
|
||||
return { content: [{ type: "text", text: `Rate limit exceeded. Retry after ${rateCheck.retryAfterMs}ms.` }] };
|
||||
}
|
||||
try {
|
||||
const result = await deployContract(
|
||||
scriptCbor,
|
||||
scriptType,
|
||||
mnemonic,
|
||||
initialDatum || null,
|
||||
lovelaceAmount || 2_000_000,
|
||||
);
|
||||
|
||||
const lucid = await lucidForAddress(makeProvider(), changeAddress);
|
||||
const result = await buildDeployContract(lucid, { scriptCbor, scriptType, initialDatum, lovelaceAmount });
|
||||
return {
|
||||
content: [{
|
||||
type: "text",
|
||||
text: `# Smart Contract Deployed
|
||||
text: `# Unsigned Contract Deployment Built
|
||||
|
||||
Transaction Hash: ${result.txHash}
|
||||
Script Address: ${result.scriptAddress}
|
||||
Script Hash: ${result.scriptHash}
|
||||
Script Type: ${result.scriptType}
|
||||
**Script Address:** ${result.scriptAddress}
|
||||
**Script Hash:** ${result.scriptHash}
|
||||
**Script Type:** ${result.scriptType}
|
||||
**Estimated Fee:** ${result.feeAda} AP3X
|
||||
**Tx Hash:** ${result.txHash}
|
||||
|
||||
Funds locked at the script address. Use vector_interact_contract to interact with this contract.
|
||||
**Unsigned TX CBOR:**
|
||||
\`\`\`
|
||||
${result.txCbor}
|
||||
\`\`\`
|
||||
|
||||
[View on Explorer](${result.links.explorer})`,
|
||||
Sign locally with vector_signer_sign, broadcast with vector_submit_transaction, then use vector_build_interact_contract to interact with the deployed contract.`,
|
||||
}],
|
||||
};
|
||||
} catch (err) {
|
||||
@@ -1296,28 +880,27 @@ Funds locked at the script address. Use vector_interact_contract to interact wit
|
||||
return {
|
||||
content: [{
|
||||
type: "text",
|
||||
text: `Failed to deploy contract: ${error.message}
|
||||
text: `Failed to build contract deployment: ${error.message}
|
||||
|
||||
**Troubleshooting Tips:**
|
||||
1. Verify the script CBOR is valid hex (compiled Aiken or Plutus output)
|
||||
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`,
|
||||
2. Ensure the wallet at changeAddress has sufficient AP3X for the locked amount + fees
|
||||
3. Check that the script type matches the compiled version (PlutusV1/V2/V3)`,
|
||||
}],
|
||||
};
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// vector_interact_contract - Interact with a deployed smart contract
|
||||
// vector_build_interact_contract - build an UNSIGNED contract interaction (keyless)
|
||||
server.tool(
|
||||
"vector_interact_contract",
|
||||
"Interact with a deployed Plutus/Aiken smart contract - lock AP3X or spend from it",
|
||||
"vector_build_interact_contract",
|
||||
"Build an UNSIGNED transaction interacting with a deployed Plutus/Aiken contract - lock AP3X at it or spend from it. Takes no key material - sign with your local signer, then broadcast with vector_submit_transaction.",
|
||||
{
|
||||
changeAddress: z.string().describe("Your wallet address (source of funds, change, and the required signer for spends). Get it from your local signer's vector_signer_get_address."),
|
||||
scriptCbor: z.string().describe("Compiled Plutus/Aiken script in CBOR hex"),
|
||||
scriptType: z.enum(["PlutusV1", "PlutusV2", "PlutusV3"]).describe("Script version"),
|
||||
action: z.enum(["spend", "lock"]).describe("'spend' to collect UTxOs from the script, 'lock' to send funds to it"),
|
||||
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 AP3X)"),
|
||||
@@ -1327,38 +910,33 @@ Funds locked at the script address. Use vector_interact_contract to interact wit
|
||||
}).optional().describe("Specific UTxO to spend from (optional, otherwise spends all UTxOs at script address)"),
|
||||
assets: z.record(z.string(), z.string()).optional().describe("Additional native assets for lock action: { 'policyId+assetNameHex': 'quantity' }"),
|
||||
},
|
||||
async ({ scriptCbor, scriptType, action, mnemonic, redeemer, datum, lovelaceAmount, utxoRef, assets }) => {
|
||||
async ({ changeAddress, scriptCbor, scriptType, action, redeemer, datum, lovelaceAmount, utxoRef, assets }) => {
|
||||
const rateCheck = rateLimiter.check();
|
||||
if (!rateCheck.allowed) {
|
||||
return { content: [{ type: "text", text: `Rate limit exceeded. Retry after ${rateCheck.retryAfterMs}ms.` }] };
|
||||
}
|
||||
try {
|
||||
const result = await interactWithContract(
|
||||
scriptCbor,
|
||||
scriptType,
|
||||
action,
|
||||
mnemonic,
|
||||
redeemer || null,
|
||||
datum || null,
|
||||
lovelaceAmount || 2_000_000,
|
||||
utxoRef || null,
|
||||
assets || null,
|
||||
);
|
||||
|
||||
const actionVerb = result.action === 'spend' ? 'collected from' : 'locked at';
|
||||
|
||||
const lucid = await lucidForAddress(makeProvider(), changeAddress);
|
||||
const result = await buildInteractContract(lucid, {
|
||||
scriptCbor, scriptType, action, changeAddress, redeemer, datum, lovelaceAmount, utxoRef, assets,
|
||||
});
|
||||
const actionVerb = result.action === 'spend' ? 'collects from' : 'locks funds at';
|
||||
return {
|
||||
content: [{
|
||||
type: "text",
|
||||
text: `# Contract Interaction Successful
|
||||
text: `# Unsigned Contract Interaction Built
|
||||
|
||||
Transaction Hash: ${result.txHash}
|
||||
Action: ${result.action}
|
||||
Script Address: ${result.scriptAddress}
|
||||
**Action:** ${result.action} (${actionVerb} the script address)
|
||||
**Script Address:** ${result.scriptAddress}
|
||||
**Estimated Fee:** ${result.feeAda} AP3X
|
||||
**Tx Hash:** ${result.txHash}
|
||||
|
||||
Funds ${actionVerb} the script address.
|
||||
**Unsigned TX CBOR:**
|
||||
\`\`\`
|
||||
${result.txCbor}
|
||||
\`\`\`
|
||||
|
||||
[View on Explorer](${result.links.explorer})`,
|
||||
Sign locally with vector_signer_sign { txCbor }, then broadcast with vector_submit_transaction { signedTxCbor }.`,
|
||||
}],
|
||||
};
|
||||
} catch (err) {
|
||||
@@ -1366,14 +944,13 @@ Funds ${actionVerb} the script address.
|
||||
return {
|
||||
content: [{
|
||||
type: "text",
|
||||
text: `Failed to interact with contract: ${error.message}
|
||||
text: `Failed to build contract interaction: ${error.message}
|
||||
|
||||
**Troubleshooting Tips:**
|
||||
1. For 'spend': ensure the script address has UTxOs and the redeemer satisfies the validator
|
||||
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`,
|
||||
2. For 'lock': ensure the wallet at changeAddress has sufficient AP3X and the datum matches the script's expectations
|
||||
3. Spending requires collateral - ensure the wallet has a pure-AP3X UTxO (no native tokens)
|
||||
4. Verify the script CBOR matches the deployed script exactly`,
|
||||
}],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -15,8 +15,11 @@ import { startServer, stopServer, ServerContext } from '../setup.ts';
|
||||
*/
|
||||
const EXPECTED_TOOLS = [
|
||||
'vector_await_transaction',
|
||||
'vector_build_deploy_contract',
|
||||
'vector_build_interact_contract',
|
||||
'vector_build_send_apex',
|
||||
'vector_build_send_tokens',
|
||||
'vector_build_transaction',
|
||||
'vector_deploy_contract',
|
||||
'vector_deregister_agent',
|
||||
'vector_discover_agents',
|
||||
'vector_dry_run',
|
||||
@@ -26,7 +29,6 @@ const EXPECTED_TOOLS = [
|
||||
'vector_get_spend_limits',
|
||||
'vector_get_transaction_history',
|
||||
'vector_get_utxos',
|
||||
'vector_interact_contract',
|
||||
'vector_message_agent',
|
||||
'vector_register_agent',
|
||||
'vector_self_improvement_analyze_metrics',
|
||||
@@ -34,8 +36,6 @@ const EXPECTED_TOOLS = [
|
||||
'vector_self_improvement_critique',
|
||||
'vector_self_improvement_endorse',
|
||||
'vector_self_improvement_submit_proposal',
|
||||
'vector_send_apex',
|
||||
'vector_send_tokens',
|
||||
'vector_submit_transaction',
|
||||
'vector_transfer_agent',
|
||||
'vector_update_agent',
|
||||
|
||||
@@ -18,9 +18,189 @@
|
||||
],
|
||||
"$schema": "http://json-schema.org/draft-07/schema#"
|
||||
},
|
||||
"vector_build_deploy_contract": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"changeAddress": {
|
||||
"description": "Your wallet address (source of funds; receives change). Get it from your local signer's vector_signer_get_address.",
|
||||
"type": "string"
|
||||
},
|
||||
"scriptCbor": {
|
||||
"description": "Compiled Plutus/Aiken script in CBOR hex format",
|
||||
"type": "string"
|
||||
},
|
||||
"scriptType": {
|
||||
"description": "Script version",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"PlutusV1",
|
||||
"PlutusV2",
|
||||
"PlutusV3"
|
||||
]
|
||||
},
|
||||
"initialDatum": {
|
||||
"description": "Initial datum as CBOR hex. Use 'd87980' for void/unit datum. Defaults to void if omitted.",
|
||||
"type": "string"
|
||||
},
|
||||
"lovelaceAmount": {
|
||||
"description": "AP3X to lock at the script address in lovelace (default: 2,000,000 = 2 AP3X)",
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"changeAddress",
|
||||
"scriptCbor",
|
||||
"scriptType"
|
||||
],
|
||||
"$schema": "http://json-schema.org/draft-07/schema#"
|
||||
},
|
||||
"vector_build_interact_contract": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"changeAddress": {
|
||||
"description": "Your wallet address (source of funds, change, and the required signer for spends). Get it from your local signer's vector_signer_get_address.",
|
||||
"type": "string"
|
||||
},
|
||||
"scriptCbor": {
|
||||
"description": "Compiled Plutus/Aiken script in CBOR hex",
|
||||
"type": "string"
|
||||
},
|
||||
"scriptType": {
|
||||
"description": "Script version",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"PlutusV1",
|
||||
"PlutusV2",
|
||||
"PlutusV3"
|
||||
]
|
||||
},
|
||||
"action": {
|
||||
"description": "'spend' to collect UTxOs from the script, 'lock' to send funds to it",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"spend",
|
||||
"lock"
|
||||
]
|
||||
},
|
||||
"redeemer": {
|
||||
"description": "Redeemer as CBOR hex (required for spend, use 'd87980' for void)",
|
||||
"type": "string"
|
||||
},
|
||||
"datum": {
|
||||
"description": "Datum as CBOR hex (required for lock, use 'd87980' for void)",
|
||||
"type": "string"
|
||||
},
|
||||
"lovelaceAmount": {
|
||||
"description": "Lovelace to lock (for lock action, default: 2,000,000 = 2 AP3X)",
|
||||
"type": "number"
|
||||
},
|
||||
"utxoRef": {
|
||||
"description": "Specific UTxO to spend from (optional, otherwise spends all UTxOs at script address)",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"txHash": {
|
||||
"type": "string"
|
||||
},
|
||||
"outputIndex": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"txHash",
|
||||
"outputIndex"
|
||||
]
|
||||
},
|
||||
"assets": {
|
||||
"description": "Additional native assets for lock action: { 'policyId+assetNameHex': 'quantity' }",
|
||||
"type": "object",
|
||||
"propertyNames": {
|
||||
"type": "string"
|
||||
},
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"changeAddress",
|
||||
"scriptCbor",
|
||||
"scriptType",
|
||||
"action"
|
||||
],
|
||||
"$schema": "http://json-schema.org/draft-07/schema#"
|
||||
},
|
||||
"vector_build_send_apex": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"changeAddress": {
|
||||
"description": "Your wallet address (source of funds; receives change). Get it from your local signer's vector_signer_get_address.",
|
||||
"type": "string"
|
||||
},
|
||||
"recipientAddress": {
|
||||
"description": "Recipient Vector address (addr1...)",
|
||||
"type": "string"
|
||||
},
|
||||
"amount": {
|
||||
"description": "Amount of AP3X to send",
|
||||
"type": "number",
|
||||
"minimum": 1
|
||||
},
|
||||
"metadata": {
|
||||
"description": "Optional transaction metadata in JSON format",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"changeAddress",
|
||||
"recipientAddress",
|
||||
"amount"
|
||||
],
|
||||
"$schema": "http://json-schema.org/draft-07/schema#"
|
||||
},
|
||||
"vector_build_send_tokens": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"changeAddress": {
|
||||
"description": "Your wallet address (source of funds; receives change). Get it from your local signer's vector_signer_get_address.",
|
||||
"type": "string"
|
||||
},
|
||||
"recipientAddress": {
|
||||
"description": "Recipient Vector address (addr1...)",
|
||||
"type": "string"
|
||||
},
|
||||
"policyId": {
|
||||
"description": "Token policy ID (56 hex characters)",
|
||||
"type": "string"
|
||||
},
|
||||
"assetName": {
|
||||
"description": "Asset name (text or hex; can be empty for policy-only tokens)",
|
||||
"type": "string"
|
||||
},
|
||||
"amount": {
|
||||
"description": "Amount of tokens to send",
|
||||
"type": "string"
|
||||
},
|
||||
"adaAmount": {
|
||||
"description": "Optional AP3X to include with the tokens (defaults to the 2 AP3X minimum)",
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"changeAddress",
|
||||
"recipientAddress",
|
||||
"policyId",
|
||||
"assetName",
|
||||
"amount"
|
||||
],
|
||||
"$schema": "http://json-schema.org/draft-07/schema#"
|
||||
},
|
||||
"vector_build_transaction": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"changeAddress": {
|
||||
"description": "Your wallet address (source of funds; receives change). Get it from your local signer's vector_signer_get_address.",
|
||||
"type": "string"
|
||||
},
|
||||
"outputs": {
|
||||
"description": "Transaction outputs",
|
||||
"minItems": 1,
|
||||
@@ -53,58 +233,14 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"mnemonic": {
|
||||
"description": "15 or 24-word BIP39 mnemonic for the signing wallet",
|
||||
"type": "string"
|
||||
},
|
||||
"metadata": {
|
||||
"description": "Optional JSON metadata (attached under label 674)",
|
||||
"type": "string"
|
||||
},
|
||||
"submit": {
|
||||
"description": "If true, sign and submit the transaction. If false/omitted, return unsigned CBOR for review.",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"outputs",
|
||||
"mnemonic"
|
||||
],
|
||||
"$schema": "http://json-schema.org/draft-07/schema#"
|
||||
},
|
||||
"vector_deploy_contract": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"scriptCbor": {
|
||||
"description": "Compiled Plutus/Aiken script in CBOR hex format",
|
||||
"type": "string"
|
||||
},
|
||||
"scriptType": {
|
||||
"description": "Script version",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"PlutusV1",
|
||||
"PlutusV2",
|
||||
"PlutusV3"
|
||||
]
|
||||
},
|
||||
"mnemonic": {
|
||||
"description": "15 or 24-word BIP39 mnemonic for the deploying wallet",
|
||||
"type": "string"
|
||||
},
|
||||
"initialDatum": {
|
||||
"description": "Initial datum as CBOR hex. Use 'd87980' for void/unit datum. Defaults to void if omitted.",
|
||||
"type": "string"
|
||||
},
|
||||
"lovelaceAmount": {
|
||||
"description": "AP3X to lock at the script address in lovelace (default: 2,000,000 = 2 AP3X)",
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"scriptCbor",
|
||||
"scriptType",
|
||||
"mnemonic"
|
||||
"changeAddress",
|
||||
"outputs"
|
||||
],
|
||||
"$schema": "http://json-schema.org/draft-07/schema#"
|
||||
},
|
||||
@@ -276,81 +412,6 @@
|
||||
},
|
||||
"$schema": "http://json-schema.org/draft-07/schema#"
|
||||
},
|
||||
"vector_interact_contract": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"scriptCbor": {
|
||||
"description": "Compiled Plutus/Aiken script in CBOR hex",
|
||||
"type": "string"
|
||||
},
|
||||
"scriptType": {
|
||||
"description": "Script version",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"PlutusV1",
|
||||
"PlutusV2",
|
||||
"PlutusV3"
|
||||
]
|
||||
},
|
||||
"action": {
|
||||
"description": "'spend' to collect UTxOs from the script, 'lock' to send funds to it",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"spend",
|
||||
"lock"
|
||||
]
|
||||
},
|
||||
"mnemonic": {
|
||||
"description": "15 or 24-word BIP39 mnemonic for the wallet",
|
||||
"type": "string"
|
||||
},
|
||||
"redeemer": {
|
||||
"description": "Redeemer as CBOR hex (required for spend, use 'd87980' for void)",
|
||||
"type": "string"
|
||||
},
|
||||
"datum": {
|
||||
"description": "Datum as CBOR hex (required for lock, use 'd87980' for void)",
|
||||
"type": "string"
|
||||
},
|
||||
"lovelaceAmount": {
|
||||
"description": "Lovelace to lock (for lock action, default: 2,000,000 = 2 AP3X)",
|
||||
"type": "number"
|
||||
},
|
||||
"utxoRef": {
|
||||
"description": "Specific UTxO to spend from (optional, otherwise spends all UTxOs at script address)",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"txHash": {
|
||||
"type": "string"
|
||||
},
|
||||
"outputIndex": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"txHash",
|
||||
"outputIndex"
|
||||
]
|
||||
},
|
||||
"assets": {
|
||||
"description": "Additional native assets for lock action: { 'policyId+assetNameHex': 'quantity' }",
|
||||
"type": "object",
|
||||
"propertyNames": {
|
||||
"type": "string"
|
||||
},
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"scriptCbor",
|
||||
"scriptType",
|
||||
"action",
|
||||
"mnemonic"
|
||||
],
|
||||
"$schema": "http://json-schema.org/draft-07/schema#"
|
||||
},
|
||||
"vector_message_agent": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -655,81 +716,6 @@
|
||||
],
|
||||
"$schema": "http://json-schema.org/draft-07/schema#"
|
||||
},
|
||||
"vector_send_apex": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"recipientAddress": {
|
||||
"description": "Recipient Vector address (addr1...)",
|
||||
"type": "string"
|
||||
},
|
||||
"amount": {
|
||||
"description": "Amount of AP3X to send",
|
||||
"type": "number",
|
||||
"minimum": 1
|
||||
},
|
||||
"mnemonic": {
|
||||
"description": "15 or 24-word BIP39 mnemonic for the sending wallet",
|
||||
"type": "string"
|
||||
},
|
||||
"metadata": {
|
||||
"description": "Optional transaction metadata in JSON format",
|
||||
"type": "string"
|
||||
},
|
||||
"unsigned_only": {
|
||||
"description": "If true, return unsigned TX CBOR without signing or submitting",
|
||||
"default": false,
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"recipientAddress",
|
||||
"amount",
|
||||
"mnemonic"
|
||||
],
|
||||
"$schema": "http://json-schema.org/draft-07/schema#"
|
||||
},
|
||||
"vector_send_tokens": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"recipientAddress": {
|
||||
"description": "Recipient Vector address (addr1...)",
|
||||
"type": "string"
|
||||
},
|
||||
"policyId": {
|
||||
"description": "Token policy ID",
|
||||
"type": "string"
|
||||
},
|
||||
"assetName": {
|
||||
"description": "Asset name (can be empty for policy-only tokens)",
|
||||
"type": "string"
|
||||
},
|
||||
"amount": {
|
||||
"description": "Amount of tokens to send",
|
||||
"type": "string"
|
||||
},
|
||||
"mnemonic": {
|
||||
"description": "15 or 24-word BIP39 mnemonic for the sending wallet",
|
||||
"type": "string"
|
||||
},
|
||||
"adaAmount": {
|
||||
"description": "Optional AP3X to include (uses minimum required if not specified)",
|
||||
"type": "number"
|
||||
},
|
||||
"unsigned_only": {
|
||||
"description": "If true, return unsigned TX CBOR without signing or submitting",
|
||||
"default": false,
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"recipientAddress",
|
||||
"policyId",
|
||||
"assetName",
|
||||
"amount",
|
||||
"mnemonic"
|
||||
],
|
||||
"$schema": "http://json-schema.org/draft-07/schema#"
|
||||
},
|
||||
"vector_submit_transaction": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
Reference in New Issue
Block a user