import { Client, Wallet } from "xrpl";
import { z } from "zod";
import { server } from "../../server/server.js";
import { getXrplClient } from "../../core/services/clients.js";
import { MAINNET_URL, TESTNET_URL } from "../../core/constants.js";
import { connectedWallet, isConnectedToTestnet } from "../../core/state.js";
// Helper to convert string to hex
const toHex = (str: string) => Buffer.from(str, "utf-8").toString("hex");
server.tool(
"credential-accept",
"Accept a credential that was issued to your account. The credential must be accepted by the subject for it to be considered valid. After acceptance, the reserve burden transfers from the issuer to the subject.",
{
fromSeed: z
.string()
.optional()
.describe(
"Optional seed of the subject's wallet. If not provided, the connected wallet will be used."
),
issuer: z
.string()
.describe(
"The account address of the credential issuer."
),
credentialType: z
.string()
.describe(
"Type of credential to accept (must match the issued credential type). Will be hex-encoded."
),
fee: z.string().optional().describe("Transaction fee in drops"),
useTestnet: z
.boolean()
.optional()
.describe(
"Whether to use the testnet (true) or mainnet (false)."
),
},
async ({
fromSeed,
issuer,
credentialType,
fee,
useTestnet,
}) => {
let client: Client | null = null;
try {
const useTestnetNetwork =
useTestnet !== undefined ? useTestnet : isConnectedToTestnet;
client = await getXrplClient(useTestnetNetwork);
let wallet: Wallet;
if (fromSeed) {
wallet = Wallet.fromSeed(fromSeed);
} else if (connectedWallet) {
wallet = connectedWallet;
} else {
throw new Error(
"No wallet connected. Please connect first using connect-to-xrpl tool or provide a fromSeed."
);
}
const tx: any = {
TransactionType: "CredentialAccept",
Account: wallet.address,
Issuer: issuer,
CredentialType: toHex(credentialType),
};
if (fee) tx.Fee = fee;
const prepared = await client.autofill(tx);
const signed = wallet.sign(prepared);
const result = await client.submitAndWait(signed.tx_blob);
let status = "unknown";
if (typeof result.result.meta !== "string" && result.result.meta) {
status =
result.result.meta.TransactionResult === "tesSUCCESS"
? "success"
: "failed";
}
return {
content: [
{
type: "text",
text: JSON.stringify(
{
status,
hash: result.result.hash,
subject: wallet.address,
issuer,
credentialType,
credentialTypeHex: toHex(credentialType),
note: "Credential is now valid and accepted. The reserve is now held by the subject.",
network: useTestnetNetwork
? TESTNET_URL
: MAINNET_URL,
networkType: useTestnetNetwork
? "testnet"
: "mainnet",
result: result.result,
},
null,
2
),
},
],
};
} catch (error) {
return {
content: [
{
type: "text",
text: `Error accepting credential: ${
error instanceof Error
? error.message
: String(error)
}`,
},
],
};
} finally {
if (client) {
await client.disconnect();
}
}
}
);