mintCoin.tsā¢7.16 kB
import { Tool } from "@modelcontextprotocol/sdk/types.js";
import { getAptosClient } from "../../config.js";
import { getDefaultAccount } from "../../utils/account.js";
import { formatAddress, formatCoinAmount } from "../../utils/format.js";
export const MINT_COIN: Tool = {
name: "mint_coin",
description: "Mint new tokens of a previously deployed custom coin. Only the coin deployer can mint new tokens. This increases the total supply of the coin and sends the minted tokens to a specified recipient.",
inputSchema: {
type: "object",
properties: {
coin_type: {
type: "string",
description: "The coin type identifier (e.g., '0x123::coin::T')",
},
recipient_address: {
type: "string",
description: "Address to receive the minted coins",
},
amount: {
type: "string",
description: "Amount of coins to mint (in the coin's base unit)",
},
max_gas_amount: {
type: "number",
description: "Maximum gas amount for the transaction (optional)",
default: 3000,
},
},
required: ["coin_type", "recipient_address", "amount"],
},
};
/**
* Mints new tokens of a custom coin
* @param args The arguments containing coin type, recipient, and amount
* @returns The minting transaction details
*/
export async function mintCoinHandler(args: Record<string, any> | undefined) {
if (!isMintCoinArgs(args)) {
throw new Error("Invalid arguments for mint_coin");
}
const { coin_type, recipient_address, amount, max_gas_amount = 3000 } = args;
try {
const results = await performMintCoin(coin_type, recipient_address, amount, max_gas_amount);
return {
content: [{ type: "text", text: results }],
isError: false,
};
} catch (error) {
return {
content: [
{
type: "text",
text: `Error minting coin: ${error instanceof Error ? error.message : String(error)}`,
},
],
isError: true,
};
}
}
/**
* Mints new tokens of a custom coin
* @param coinType The coin type identifier
* @param recipientAddress The address to receive minted coins
* @param amount The amount to mint
* @param maxGasAmount Maximum gas amount for the transaction
* @returns The minting transaction details as a formatted string
*/
export async function performMintCoin(
coinType: string,
recipientAddress: string,
amount: string,
maxGasAmount: number = 3000
): Promise<string> {
try {
const aptos = getAptosClient();
const minterAccount = getDefaultAccount();
const minterAddress = minterAccount.accountAddress.toString();
// Validate inputs
if (!coinType || coinType.trim().length === 0) {
throw new Error("Coin type cannot be empty");
}
if (!recipientAddress || recipientAddress.trim().length === 0) {
throw new Error("Recipient address cannot be empty");
}
if (!amount || amount.trim().length === 0) {
throw new Error("Amount cannot be empty");
}
// Validate amount is a valid number
const amountNum = BigInt(amount);
if (amountNum <= 0) {
throw new Error("Amount must be greater than 0");
}
// Check if the minter is the coin deployer by extracting address from coin type
const coinTypeMatch = coinType.match(/^(0x[a-fA-F0-9]+)::/);
if (!coinTypeMatch) {
throw new Error("Invalid coin type format. Expected format: 0xADDRESS::module::Type");
}
const coinDeployerAddress = coinTypeMatch[1].toLowerCase();
const normalizedMinterAddress = minterAddress.toLowerCase();
if (coinDeployerAddress !== normalizedMinterAddress) {
throw new Error(`Only the coin deployer (${coinDeployerAddress}) can mint this coin. Current account: ${normalizedMinterAddress}`);
}
// Build the mint transaction
const transaction = await aptos.transaction.build.simple({
sender: minterAccount.accountAddress,
data: {
function: "0x1::managed_coin::mint",
typeArguments: [coinType],
functionArguments: [recipientAddress, amount],
},
options: {
maxGasAmount,
},
});
// Sign and submit the transaction
const committedTxn = await aptos.signAndSubmitTransaction({
signer: minterAccount,
transaction,
});
// Wait for transaction confirmation
const executedTxn = await aptos.waitForTransaction({
transactionHash: committedTxn.hash,
});
let result = `šŖ Coin Minting Successful!
Minting Details:
Coin Type: ${coinType}
Minter: ${formatAddress(minterAddress)}
Recipient: ${formatAddress(recipientAddress)}
Amount Minted: ${amount}
Transaction Information:
Transaction Hash: ${committedTxn.hash}
Gas Used: ${executedTxn.gas_used}
Status: ${executedTxn.success ? 'Success' : 'Failed'}`;
// Note: Coin info formatting is not available in current SDK version
// The amount is shown in the coin's base unit
result += `
š Next Steps:
1. The recipient can now check their balance using get_coin_balance
2. The recipient can transfer these coins using transfer_coin
3. Mint more coins as needed (only you as the deployer can mint)
ā ļø Important Notes:
- Only the coin deployer can mint new tokens
- The recipient must have registered for this coin type to receive it
- Minting increases the total supply of the coin
- Keep track of total minted amounts for tokenomics
ā
Coins successfully minted and sent to ${formatAddress(recipientAddress)}!`;
return result;
} catch (error) {
console.error('Error minting coin:', error);
if (error instanceof Error) {
if (error.message.includes('insufficient')) {
throw new Error("Insufficient APT balance to pay for minting transaction fees");
}
if (error.message.includes('not registered')) {
throw new Error("Recipient account is not registered for this coin type. They need to register first.");
}
if (error.message.includes('unauthorized') || error.message.includes('permission')) {
throw new Error("Only the coin deployer can mint new tokens of this coin");
}
if (error.message.includes('not found')) {
throw new Error("Coin type not found. Make sure the coin has been deployed and the type is correct.");
}
}
throw new Error(`Failed to mint coin: ${error instanceof Error ? error.message : String(error)}`);
}
}
/**
* Checks if the provided arguments are valid for the mintCoin tool
* @param args The arguments to check
* @returns True if the arguments are valid, false otherwise
*/
export function isMintCoinArgs(args: unknown): args is {
coin_type: string;
recipient_address: string;
amount: string;
max_gas_amount?: number;
} {
return (
typeof args === "object" &&
args !== null &&
"coin_type" in args &&
typeof (args as any).coin_type === "string" &&
"recipient_address" in args &&
typeof (args as any).recipient_address === "string" &&
"amount" in args &&
typeof (args as any).amount === "string" &&
(!(args as any).max_gas_amount || typeof (args as any).max_gas_amount === "number")
);
}