@gala-chain/launchpad-mcp-server
Version:
MCP server for Gala Launchpad - 102 tools (pool management, event watchers, GSwap DEX trading, price history, token creation, wallet management, DEX pool discovery, liquidity positions, token locks, locked token queries, composite pool data, cross-chain b
363 lines • 12.8 kB
JavaScript
;
/**
* Validation Utilities for MCP Prompts
*
* Input validation functions for slash command arguments.
* Prevents malformed inputs and provides clear error messages.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.ValidationError = void 0;
exports.validateTokenName = validateTokenName;
exports.validateNumericAmount = validateNumericAmount;
exports.validateSlippage = validateSlippage;
exports.validateAddress = validateAddress;
exports.validateTokenSymbol = validateTokenSymbol;
exports.validatePaginationLimit = validatePaginationLimit;
exports.validateTokenList = validateTokenList;
exports.validateUrl = validateUrl;
exports.safeValidate = safeValidate;
exports.validateOptional = validateOptional;
exports.validateOptionalSlippage = validateOptionalSlippage;
exports.validateOptionalLimit = validateOptionalLimit;
exports.validateOptionalAddress = validateOptionalAddress;
/**
* Validation error class for better error handling
*/
class ValidationError extends Error {
field;
constructor(field, message) {
super(`Validation error for '${field}': ${message}`);
this.field = field;
this.name = 'ValidationError';
}
}
exports.ValidationError = ValidationError;
/**
* Validate token name format
*
* Token names must be 2-20 characters, alphanumeric with hyphens and underscores.
*
* @param name - Token name to validate
* @param fieldName - Field name for error messages (default: 'tokenName')
* @throws {ValidationError} If token name is invalid
*
* @example
* ```typescript
* validateTokenName('anime'); // ✅ Valid
* validateTokenName('test-123'); // ✅ Valid
* validateTokenName('my_token'); // ✅ Valid
* validateTokenName('a'); // ❌ Throws: too short
* validateTokenName('token@#$'); // ❌ Throws: invalid characters
* ```
*/
function validateTokenName(name, fieldName = 'tokenName') {
if (!name || typeof name !== 'string') {
throw new ValidationError(fieldName, 'Token name is required');
}
if (name.length < 2 || name.length > 20) {
throw new ValidationError(fieldName, `Token name must be 2-20 characters (got ${name.length})`);
}
const tokenNameRegex = /^[a-zA-Z0-9-_]+$/;
if (!tokenNameRegex.test(name)) {
throw new ValidationError(fieldName, 'Token name can only contain letters, numbers, hyphens, and underscores');
}
}
/**
* Validate numeric amount
*
* Amounts must be positive numbers (integers or decimals).
*
* @param amount - Amount to validate (string or number)
* @param fieldName - Field name for error messages
* @throws {ValidationError} If amount is invalid
*
* @example
* ```typescript
* validateNumericAmount('100', 'galaAmount'); // ✅ Valid
* validateNumericAmount('99.5', 'tokenAmount'); // ✅ Valid
* validateNumericAmount('-5', 'amount'); // ❌ Throws: negative
* validateNumericAmount('abc', 'amount'); // ❌ Throws: not a number
* ```
*/
function validateNumericAmount(amount, fieldName) {
if (amount === null || amount === undefined || amount === '') {
throw new ValidationError(fieldName, 'Amount is required');
}
const numAmount = typeof amount === 'string' ? parseFloat(amount) : amount;
if (isNaN(numAmount)) {
throw new ValidationError(fieldName, `Amount must be a valid number (got: ${amount})`);
}
if (numAmount <= 0) {
throw new ValidationError(fieldName, `Amount must be positive (got: ${numAmount})`);
}
if (!isFinite(numAmount)) {
throw new ValidationError(fieldName, 'Amount must be a finite number');
}
}
/**
* Validate slippage tolerance percentage
*
* Slippage must be between 0.01% and 100%.
*
* @param slippage - Slippage percentage to validate
* @param fieldName - Field name for error messages (default: 'slippage')
* @throws {ValidationError} If slippage is invalid
*
* @example
* ```typescript
* validateSlippage('1'); // ✅ Valid (1%)
* validateSlippage('0.5'); // ✅ Valid (0.5%)
* validateSlippage('50'); // ✅ Valid (50%)
* validateSlippage('0'); // ❌ Throws: too low
* validateSlippage('150'); // ❌ Throws: too high
* ```
*/
function validateSlippage(slippage, fieldName = 'slippage') {
if (slippage === null || slippage === undefined || slippage === '') {
throw new ValidationError(fieldName, 'Slippage is required');
}
const numSlippage = typeof slippage === 'string' ? parseFloat(slippage) : slippage;
if (isNaN(numSlippage)) {
throw new ValidationError(fieldName, `Slippage must be a valid number (got: ${slippage})`);
}
if (numSlippage < 0.01 || numSlippage > 100) {
throw new ValidationError(fieldName, `Slippage must be between 0.01% and 100% (got: ${numSlippage}%)`);
}
}
/**
* Validate wallet address format
*
* Supports both GalaChain format (eth|0x...) and standard Ethereum format (0x...).
*
* @param address - Wallet address to validate
* @param fieldName - Field name for error messages (default: 'address')
* @throws {ValidationError} If address is invalid
*
* @example
* ```typescript
* validateAddress('eth|0x1234567890abcdef1234567890abcdef12345678'); // ✅ Valid
* validateAddress('0x1234567890abcdef1234567890abcdef12345678'); // ✅ Valid
* validateAddress('invalid'); // ❌ Throws: invalid format
* ```
*/
function validateAddress(address, fieldName = 'address') {
if (!address || typeof address !== 'string') {
throw new ValidationError(fieldName, 'Address is required');
}
// GalaChain format: eth|0x{40 hex chars}
const galachainRegex = /^eth\|0x[a-fA-F0-9]{40}$/;
// Standard Ethereum format: 0x{40 hex chars}
const ethereumRegex = /^0x[a-fA-F0-9]{40}$/;
if (!galachainRegex.test(address) && !ethereumRegex.test(address)) {
throw new ValidationError(fieldName, 'Address must be in GalaChain format (eth|0x...) or Ethereum format (0x...)');
}
}
/**
* Validate token symbol format
*
* Symbols must be 1-8 uppercase letters.
*
* @param symbol - Token symbol to validate
* @param fieldName - Field name for error messages (default: 'symbol')
* @throws {ValidationError} If symbol is invalid
*
* @example
* ```typescript
* validateTokenSymbol('GALA'); // ✅ Valid
* validateTokenSymbol('TEST'); // ✅ Valid
* validateTokenSymbol('test'); // ❌ Throws: must be uppercase
* validateTokenSymbol('TOOLONG123'); // ❌ Throws: too long
* ```
*/
function validateTokenSymbol(symbol, fieldName = 'symbol') {
if (!symbol || typeof symbol !== 'string') {
throw new ValidationError(fieldName, 'Symbol is required');
}
if (symbol.length < 1 || symbol.length > 8) {
throw new ValidationError(fieldName, `Symbol must be 1-8 characters (got ${symbol.length})`);
}
const symbolRegex = /^[A-Z]+$/;
if (!symbolRegex.test(symbol)) {
throw new ValidationError(fieldName, 'Symbol must be uppercase letters only (A-Z)');
}
}
/**
* Validate pagination limit
*
* Limits must be positive integers within reasonable range (1-100).
*
* @param limit - Pagination limit to validate
* @param max - Maximum allowed limit (default: 100)
* @param fieldName - Field name for error messages (default: 'limit')
* @throws {ValidationError} If limit is invalid
*
* @example
* ```typescript
* validatePaginationLimit('20'); // ✅ Valid
* validatePaginationLimit('1'); // ✅ Valid
* validatePaginationLimit('0'); // ❌ Throws: too low
* validatePaginationLimit('150'); // ❌ Throws: too high
* ```
*/
function validatePaginationLimit(limit, max = 100, fieldName = 'limit') {
if (limit === null || limit === undefined || limit === '') {
throw new ValidationError(fieldName, 'Limit is required');
}
const numLimit = typeof limit === 'string' ? parseInt(limit, 10) : limit;
if (isNaN(numLimit) || !Number.isInteger(numLimit)) {
throw new ValidationError(fieldName, `Limit must be a valid integer (got: ${limit})`);
}
if (numLimit < 1 || numLimit > max) {
throw new ValidationError(fieldName, `Limit must be between 1 and ${max} (got: ${numLimit})`);
}
}
/**
* Validate comma-separated token list
*
* For batch operations that accept multiple token names.
*
* @param tokens - Comma-separated token names
* @param fieldName - Field name for error messages (default: 'tokens')
* @throws {ValidationError} If token list is invalid
*
* @example
* ```typescript
* validateTokenList('anime,test,dragon'); // ✅ Valid
* validateTokenList('token1'); // ✅ Valid (single token)
* validateTokenList(''); // ❌ Throws: empty list
* validateTokenList('token1,invalid@#$'); // ❌ Throws: invalid token name
* ```
*/
function validateTokenList(tokens, fieldName = 'tokens') {
if (!tokens || typeof tokens !== 'string') {
throw new ValidationError(fieldName, 'Token list is required');
}
const tokenArray = tokens.split(',').map((t) => t.trim());
if (tokenArray.length === 0 || tokenArray.some((t) => !t)) {
throw new ValidationError(fieldName, 'Token list cannot be empty');
}
// Validate each token name
tokenArray.forEach((token, index) => {
try {
validateTokenName(token, `${fieldName}[${index}]`);
}
catch (error) {
if (error instanceof ValidationError) {
throw new ValidationError(fieldName, `Invalid token at position ${index + 1}: ${token}`);
}
throw error;
}
});
return tokenArray;
}
/**
* Validate URL format
*
* URLs must be well-formed HTTP/HTTPS URLs.
*
* @param url - URL to validate
* @param fieldName - Field name for error messages (default: 'url')
* @throws {ValidationError} If URL is invalid
*
* @example
* ```typescript
* validateUrl('https://example.com'); // ✅ Valid
* validateUrl('https://twitter.com/user'); // ✅ Valid
* validateUrl('http://localhost:3000'); // ✅ Valid
* validateUrl('not-a-url'); // ❌ Throws: invalid format
* ```
*/
function validateUrl(url, fieldName = 'url') {
if (!url || typeof url !== 'string') {
throw new ValidationError(fieldName, 'URL is required');
}
try {
const urlObj = new URL(url);
if (!['http:', 'https:'].includes(urlObj.protocol)) {
throw new ValidationError(fieldName, 'URL must use HTTP or HTTPS protocol');
}
}
catch (error) {
if (error instanceof ValidationError) {
throw error;
}
throw new ValidationError(fieldName, `Invalid URL format: ${url}`);
}
}
/**
* Safe validation wrapper that doesn't throw
*
* Useful for non-critical validation where you want to continue with a warning.
*
* @param validationFn - Validation function to execute
* @returns Object with success flag and optional error
*
* @example
* ```typescript
* const result = safeValidate(() => validateTokenName('anime'));
* if (!result.success) {
* console.warn('Validation warning:', result.error);
* }
* ```
*/
function safeValidate(validationFn) {
try {
validationFn();
return { success: true };
}
catch (error) {
if (error instanceof ValidationError) {
return { success: false, error: error.message };
}
return { success: false, error: String(error) };
}
}
/**
* Validate optional field if present
*
* Convenience wrapper for validating optional fields. Only runs validation
* if the value is defined and not null.
*
* @param value - Value to validate (can be undefined)
* @param validator - Validation function to run if value is present
* @throws {ValidationError} If validation fails
*
* @example
* ```typescript
* validateOptional(args.slippage, (val) => validateSlippage(val));
* ```
*/
function validateOptional(value, validator) {
if (value !== undefined && value !== null && value !== '') {
validator(value);
}
}
/**
* Validate optional slippage field
*
* @param slippage - Slippage value to validate (optional)
* @throws {ValidationError} If slippage is invalid
*/
function validateOptionalSlippage(slippage) {
validateOptional(slippage, (val) => validateSlippage(val));
}
/**
* Validate optional pagination limit field
*
* @param limit - Limit value to validate (optional)
* @param max - Maximum allowed limit (default: 100)
* @throws {ValidationError} If limit is invalid
*/
function validateOptionalLimit(limit, max = 100) {
validateOptional(limit, (val) => validatePaginationLimit(val, max));
}
/**
* Validate optional address field
*
* @param address - Address value to validate (optional)
* @throws {ValidationError} If address is invalid
*/
function validateOptionalAddress(address) {
validateOptional(address, (val) => validateAddress(val));
}
//# sourceMappingURL=validation.js.map