Skip to main content
Glama
zetrix-contract-generator.ts47.2 kB
import { writeFileSync, mkdirSync, existsSync } from "fs"; import { join } from "path"; export interface ContractClass { name: string; type: "interface" | "library" | "contract"; extends?: string[]; properties: { name: string; visibility: "private" | "protected" | "public"; type: string; description: string; }[]; methods: { name: string; visibility: "private" | "protected" | "public"; params: { name: string; type: string }[]; returns?: string; description: string; }[]; } export interface ContractGenerationOptions { contractName: string; contractType: "token" | "nft" | "defi" | "governance" | "marketplace" | "custom"; description?: string; features?: string[]; tokenStandard?: "ZTP20" | "ZTP721" | "ZTP1155"; includeTests?: boolean; outputDirectory?: string; customClasses?: ContractClass[]; } export class ZetrixContractGenerator { /** * Generate class diagram in Mermaid format */ private generateClassDiagram(classes: ContractClass[]): string { let diagram = `# Contract Architecture - Class Diagram \`\`\`mermaid classDiagram `; // Define classes for (const cls of classes) { diagram += ` class ${cls.name} {\n`; // Private properties first const privateProps = cls.properties.filter(p => p.visibility === "private"); const protectedProps = cls.properties.filter(p => p.visibility === "protected"); const publicProps = cls.properties.filter(p => p.visibility === "public"); for (const prop of [...privateProps, ...protectedProps, ...publicProps]) { const symbol = prop.visibility === "private" ? "-" : prop.visibility === "protected" ? "#" : "+"; diagram += ` ${symbol}${prop.name} ${prop.type}\n`; } // Private methods first const privateMethods = cls.methods.filter(m => m.visibility === "private"); const protectedMethods = cls.methods.filter(m => m.visibility === "protected"); const publicMethods = cls.methods.filter(m => m.visibility === "public"); for (const method of [...privateMethods, ...protectedMethods, ...publicMethods]) { const symbol = method.visibility === "private" ? "-" : method.visibility === "protected" ? "#" : "+"; const params = method.params.map(p => `${p.name}: ${p.type}`).join(", "); const returns = method.returns ? `: ${method.returns}` : ""; diagram += ` ${symbol}${method.name}(${params})${returns}\n`; } diagram += ` }\n`; } // Define relationships for (const cls of classes) { if (cls.extends && cls.extends.length > 0) { for (const parent of cls.extends) { diagram += ` ${parent} <|-- ${cls.name}\n`; } } } diagram += `\`\`\`\n\n`; // Add descriptions diagram += `## Class Descriptions\n\n`; for (const cls of classes) { diagram += `### ${cls.name} (${cls.type})\n\n`; if (cls.properties.length > 0) { diagram += `**Properties:**\n`; for (const prop of cls.properties) { diagram += `- \`${prop.name}\` (${prop.visibility}): ${prop.description}\n`; } diagram += `\n`; } if (cls.methods.length > 0) { diagram += `**Methods:**\n`; for (const method of cls.methods) { const params = method.params.map(p => p.name).join(", "); diagram += `- \`${method.name}(${params})\` (${method.visibility}): ${method.description}\n`; } diagram += `\n`; } } return diagram; } /** * Analyze contract requirements and generate class structure */ private analyzeAndGenerateClasses(options: ContractGenerationOptions): ContractClass[] { const { contractName, contractType, features = [], tokenStandard, customClasses } = options; if (customClasses && customClasses.length > 0) { return customClasses; } const classes: ContractClass[] = []; // Base interface const baseInterface: ContractClass = { name: `I${contractName}`, type: "interface", properties: [], methods: [ { name: "contractInfo", visibility: "public", params: [], returns: "object", description: "Returns contract metadata" } ] }; // Storage library const storageLib: ContractClass = { name: `${contractName}Storage`, type: "library", properties: [ { name: "CONTRACT_INFO_KEY", visibility: "private", type: "string", description: "Key for contract info storage" } ], methods: [ { name: "_makeKey", visibility: "private", params: [{ name: "prefix", type: "string" }, { name: "parts", type: "string[]" }], returns: "string", description: "Creates storage key from prefix and parts" }, { name: "_loadOrDefault", visibility: "private", params: [{ name: "key", type: "string" }, { name: "defaultValue", type: "any" }], returns: "any", description: "Loads value or returns default" }, { name: "loadJSON", visibility: "public", params: [{ name: "key", type: "string" }], returns: "object", description: "Loads and parses JSON from storage" }, { name: "storeJSON", visibility: "public", params: [{ name: "key", type: "string" }, { name: "obj", type: "object" }], description: "Stores object as JSON" } ] }; // Validation library const validationLib: ContractClass = { name: `${contractName}Validation`, type: "library", properties: [], methods: [ { name: "_validateAddress", visibility: "private", params: [{ name: "address", type: "string" }], returns: "boolean", description: "Validates address format" }, { name: "_validateAmount", visibility: "private", params: [{ name: "amount", type: "string" }], returns: "boolean", description: "Validates amount is positive integer" }, { name: "requireValidAddress", visibility: "public", params: [{ name: "address", type: "string" }, { name: "paramName", type: "string" }], description: "Asserts address is valid" }, { name: "requireValidAmount", visibility: "public", params: [{ name: "amount", type: "string" }, { name: "paramName", type: "string" }], description: "Asserts amount is valid" } ] }; classes.push(baseInterface, storageLib, validationLib); // Add feature-specific classes if (features.includes("ownable")) { const ownableClass: ContractClass = { name: `${contractName}Ownable`, type: "library", properties: [ { name: "OWNER_KEY", visibility: "private", type: "string", description: "Storage key for owner address" } ], methods: [ { name: "_getOwner", visibility: "private", params: [], returns: "string", description: "Gets current owner from storage" }, { name: "_setOwner", visibility: "private", params: [{ name: "newOwner", type: "string" }], description: "Sets new owner in storage" }, { name: "requireOwner", visibility: "public", params: [], description: "Asserts caller is owner" }, { name: "transferOwnership", visibility: "public", params: [{ name: "newOwner", type: "string" }], description: "Transfers ownership to new address" }, { name: "owner", visibility: "public", params: [], returns: "string", description: "Returns current owner address" } ] }; classes.push(ownableClass); // Add methods to interface baseInterface.methods.push( { name: "owner", visibility: "public", params: [], returns: "string", description: "Query owner" }, { name: "transferOwnership", visibility: "public", params: [{ name: "newOwner", type: "string" }], description: "Transfer ownership" } ); } if (features.includes("pausable")) { const pausableClass: ContractClass = { name: `${contractName}Pausable`, type: "library", properties: [ { name: "PAUSE_KEY", visibility: "private", type: "string", description: "Storage key for pause state" } ], methods: [ { name: "_isPaused", visibility: "private", params: [], returns: "boolean", description: "Checks if contract is paused" }, { name: "_setPaused", visibility: "private", params: [{ name: "paused", type: "boolean" }], description: "Sets pause state" }, { name: "requireNotPaused", visibility: "public", params: [], description: "Asserts contract is not paused" }, { name: "pause", visibility: "public", params: [], description: "Pauses the contract" }, { name: "unpause", visibility: "public", params: [], description: "Unpauses the contract" }, { name: "isPaused", visibility: "public", params: [], returns: "boolean", description: "Returns pause state" } ] }; classes.push(pausableClass); baseInterface.methods.push( { name: "isPaused", visibility: "public", params: [], returns: "boolean", description: "Query pause state" }, { name: "pause", visibility: "public", params: [], description: "Pause contract" }, { name: "unpause", visibility: "public", params: [], description: "Unpause contract" } ); } if (features.includes("whitelist")) { const whitelistClass: ContractClass = { name: `${contractName}Whitelist`, type: "library", properties: [ { name: "WHITELIST_PREFIX", visibility: "private", type: "string", description: "Prefix for whitelist storage keys" } ], methods: [ { name: "_makeWhitelistKey", visibility: "private", params: [{ name: "address", type: "string" }], returns: "string", description: "Creates whitelist storage key" }, { name: "_isWhitelisted", visibility: "private", params: [{ name: "address", type: "string" }], returns: "boolean", description: "Checks whitelist status" }, { name: "requireWhitelisted", visibility: "public", params: [{ name: "address", type: "string" }], description: "Asserts address is whitelisted" }, { name: "addToWhitelist", visibility: "public", params: [{ name: "address", type: "string" }], description: "Adds address to whitelist" }, { name: "removeFromWhitelist", visibility: "public", params: [{ name: "address", type: "string" }], description: "Removes address from whitelist" }, { name: "isWhitelisted", visibility: "public", params: [{ name: "address", type: "string" }], returns: "boolean", description: "Returns whitelist status" } ] }; classes.push(whitelistClass); baseInterface.methods.push( { name: "isWhitelisted", visibility: "public", params: [{ name: "address", type: "string" }], returns: "boolean", description: "Query whitelist status" }, { name: "addToWhitelist", visibility: "public", params: [{ name: "address", type: "string" }], description: "Add to whitelist" }, { name: "removeFromWhitelist", visibility: "public", params: [{ name: "address", type: "string" }], description: "Remove from whitelist" } ); } if (features.includes("blacklist")) { const blacklistClass: ContractClass = { name: `${contractName}Blacklist`, type: "library", properties: [ { name: "BLACKLIST_PREFIX", visibility: "private", type: "string", description: "Prefix for blacklist storage keys" } ], methods: [ { name: "_makeBlacklistKey", visibility: "private", params: [{ name: "address", type: "string" }], returns: "string", description: "Creates blacklist storage key" }, { name: "_isBlacklisted", visibility: "private", params: [{ name: "address", type: "string" }], returns: "boolean", description: "Checks blacklist status" }, { name: "requireNotBlacklisted", visibility: "public", params: [{ name: "address", type: "string" }], description: "Asserts address is not blacklisted" }, { name: "addToBlacklist", visibility: "public", params: [{ name: "address", type: "string" }], description: "Adds address to blacklist" }, { name: "removeFromBlacklist", visibility: "public", params: [{ name: "address", type: "string" }], description: "Removes address from blacklist" }, { name: "isBlacklisted", visibility: "public", params: [{ name: "address", type: "string" }], returns: "boolean", description: "Returns blacklist status" } ] }; classes.push(blacklistClass); baseInterface.methods.push( { name: "isBlacklisted", visibility: "public", params: [{ name: "address", type: "string" }], returns: "boolean", description: "Query blacklist status" }, { name: "addToBlacklist", visibility: "public", params: [{ name: "address", type: "string" }], description: "Add to blacklist" }, { name: "removeFromBlacklist", visibility: "public", params: [{ name: "address", type: "string" }], description: "Remove from blacklist" } ); } // Main contract class const mainContract: ContractClass = { name: contractName, type: "contract", extends: [`I${contractName}`], properties: [], methods: [ { name: "init", visibility: "public", params: [{ name: "input_str", type: "string" }], description: "Contract initialization" }, { name: "main", visibility: "public", params: [{ name: "input_str", type: "string" }], description: "Main entry point for transactions" }, { name: "query", visibility: "public", params: [{ name: "input_str", type: "string" }], returns: "string", description: "Query entry point" } ] }; classes.push(mainContract); return classes; } /** * Generate library file following ES5 pattern */ private generateLibrary(className: string, classInfo: ContractClass): string { let code = `"use strict"; // ============================================================================ // ${className} - ${classInfo.type === "library" ? "Utility Library" : "Module"} // ============================================================================ // // This module provides reusable utility functions for the ${className.replace(/Storage|Validation|Ownable|Pausable|Whitelist|Blacklist/, "")} contract. // All functions follow ES5 patterns for compatibility with Zetrix VM. // `; // Generate the library using ES5 constructor pattern code += `const ${className} = function () {\n`; code += ` const self = this;\n\n`; code += ` // Private constants\n`; // Private properties for (const prop of classInfo.properties.filter(p => p.visibility === "private")) { code += ` const ${prop.name} = "${prop.name.toLowerCase().replace(/_/g, "-")}";\n`; } code += `\n // Private helper methods (defined first)\n`; // Private methods for (const method of classInfo.methods.filter(m => m.visibility === "private")) { const params = method.params.map(p => p.name).join(", "); code += ` const ${method.name} = function (${params}) {\n`; code += ` // ${method.description}\n`; // Add method implementation based on method name if (method.name.includes("makeKey") || method.name.includes("Key")) { code += ` return ${method.params[0]?.name || "prefix"} + "_" + ${method.params.slice(1).map(p => p.name).join(' + "_" + ') || '""'};\n`; } else if (method.name.includes("load")) { code += ` var value = Chain.load(key);\n`; code += ` return value === false ? defaultValue : value;\n`; } else if (method.name.includes("validate")) { if (method.name.includes("Address")) { code += ` return Utils.addressCheck(address);\n`; } else if (method.name.includes("Amount")) { code += ` if (!Utils.stoI64Check(amount)) return false;\n`; code += ` return Utils.int64Compare(amount, "0") > 0;\n`; } } else if (method.name.includes("get") && method.name.includes("Owner")) { code += ` var owner = Chain.load(OWNER_KEY);\n`; code += ` return owner === false ? "" : owner;\n`; } else if (method.name.includes("set") && method.name.includes("Owner")) { code += ` Chain.store(OWNER_KEY, newOwner);\n`; } else if (method.name.includes("isPaused")) { code += ` var paused = Chain.load(PAUSE_KEY);\n`; code += ` return paused === "true";\n`; } else if (method.name.includes("setPaused")) { code += ` Chain.store(PAUSE_KEY, paused ? "true" : "false");\n`; } else if (method.name.includes("isWhitelisted")) { code += ` var key = _makeWhitelistKey(address);\n`; code += ` var value = Chain.load(key);\n`; code += ` return value === "true";\n`; } else if (method.name.includes("isBlacklisted")) { code += ` var key = _makeBlacklistKey(address);\n`; code += ` var value = Chain.load(key);\n`; code += ` return value === "true";\n`; } else { code += ` // TODO: Implement ${method.name}\n`; code += ` throw "Not implemented: ${method.name}";\n`; } code += ` };\n\n`; } code += ` // Public API methods\n`; // Public methods const publicMethods = classInfo.methods.filter(m => m.visibility === "public"); for (const method of publicMethods) { const params = method.params.map(p => p.name).join(", "); code += ` self.${method.name} = function (${params}) {\n`; code += ` // ${method.description}\n`; // Add implementation if (method.name === "loadJSON") { code += ` var value = Chain.load(key);\n`; code += ` return value === false ? null : JSON.parse(value);\n`; } else if (method.name === "storeJSON") { code += ` Chain.store(key, JSON.stringify(obj));\n`; } else if (method.name.startsWith("require")) { if (method.name.includes("ValidAddress")) { code += ` Utils.assert(_validateAddress(address), paramName + " must be a valid address");\n`; } else if (method.name.includes("ValidAmount")) { code += ` Utils.assert(_validateAmount(amount), paramName + " must be a valid positive amount");\n`; } else if (method.name.includes("Owner")) { code += ` var owner = _getOwner();\n`; code += ` Utils.assert(Chain.msg.sender === owner, "Only owner can perform this action");\n`; } else if (method.name.includes("NotPaused")) { code += ` Utils.assert(!_isPaused(), "Contract is paused");\n`; } else if (method.name.includes("Whitelisted")) { code += ` Utils.assert(_isWhitelisted(address), "Address is not whitelisted: " + address);\n`; } else if (method.name.includes("NotBlacklisted")) { code += ` Utils.assert(!_isBlacklisted(address), "Address is blacklisted: " + address);\n`; } } else if (method.name === "owner") { code += ` return _getOwner();\n`; } else if (method.name === "transferOwnership") { code += ` this.requireOwner();\n`; code += ` Utils.assert(Utils.addressCheck(newOwner), "Invalid owner address");\n`; code += ` var oldOwner = _getOwner();\n`; code += ` _setOwner(newOwner);\n`; code += ` Chain.tlog("OwnershipTransferred", oldOwner, newOwner);\n`; } else if (method.name === "isPaused") { code += ` return _isPaused();\n`; } else if (method.name === "pause") { code += ` Utils.assert(!_isPaused(), "Already paused");\n`; code += ` _setPaused(true);\n`; code += ` Chain.tlog("Paused", Chain.msg.sender);\n`; } else if (method.name === "unpause") { code += ` Utils.assert(_isPaused(), "Not paused");\n`; code += ` _setPaused(false);\n`; code += ` Chain.tlog("Unpaused", Chain.msg.sender);\n`; } else if (method.name === "isWhitelisted") { code += ` return _isWhitelisted(address);\n`; } else if (method.name === "addToWhitelist") { code += ` Utils.assert(!_isWhitelisted(address), "Already whitelisted");\n`; code += ` var key = _makeWhitelistKey(address);\n`; code += ` Chain.store(key, "true");\n`; code += ` Chain.tlog("Whitelisted", address);\n`; } else if (method.name === "removeFromWhitelist") { code += ` Utils.assert(_isWhitelisted(address), "Not whitelisted");\n`; code += ` var key = _makeWhitelistKey(address);\n`; code += ` Chain.store(key, "false");\n`; code += ` Chain.tlog("RemovedFromWhitelist", address);\n`; } else if (method.name === "isBlacklisted") { code += ` return _isBlacklisted(address);\n`; } else if (method.name === "addToBlacklist") { code += ` Utils.assert(!_isBlacklisted(address), "Already blacklisted");\n`; code += ` var key = _makeBlacklistKey(address);\n`; code += ` Chain.store(key, "true");\n`; code += ` Chain.tlog("Blacklisted", address);\n`; } else if (method.name === "removeFromBlacklist") { code += ` Utils.assert(_isBlacklisted(address), "Not blacklisted");\n`; code += ` var key = _makeBlacklistKey(address);\n`; code += ` Chain.store(key, "false");\n`; code += ` Chain.tlog("RemovedFromBlacklist", address);\n`; } else { code += ` // TODO: Implement ${method.name}\n`; code += ` throw "Not implemented: ${method.name}";\n`; } code += ` };\n`; } code += `};\n`; return code; } /** * Generate main contract following standards */ private generateMainContract(options: ContractGenerationOptions, classes: ContractClass[]): string { const { contractName, description, features = [] } = options; const mainClass = classes.find(c => c.name === contractName); if (!mainClass) throw new Error("Main contract class not found"); let code = `"use strict"; // ============================================================================ // ${contractName} Smart Contract // ============================================================================ // // ${description || `A production-ready smart contract for ${contractName}`} // // Architecture: // - Follows ES5 patterns for Zetrix VM compatibility // - Modular design with separate libraries for each feature // - Private methods defined before public methods // - Comprehensive validation and error handling // // Features: ${features.join(", ") || "Basic"} // // ============================================================================ // ============================================================================ // PRIVATE METHODS (Internal Use Only) // ============================================================================ // Private helper: Initialize contract state function _initializeContract(params) { // Validate required parameters Utils.assert(params.name !== undefined, "Contract name is required"); Utils.assert(params.symbol !== undefined, "Contract symbol is required"); ${features.includes("ownable") ? 'Utils.assert(params.owner !== undefined, "Owner address is required");\n Utils.assert(Utils.addressCheck(params.owner), "Invalid owner address");' : ''} // Store contract info var contractInfo = { name: params.name, symbol: params.symbol, ${features.includes("ownable") ? 'owner: params.owner,' : ''} version: "1.0.0", deployedAt: Chain.block.timestamp }; ${contractName}Storage.storeJSON(${contractName}Storage.CONTRACT_INFO_KEY, contractInfo); ${features.includes("ownable") ? '// Initialize owner\n ${contractName}Ownable._setOwner(params.owner);' : ''} ${features.includes("pausable") ? '// Initialize as unpaused\n ${contractName}Pausable._setPaused(false);' : ''} Chain.tlog("ContractInitialized", params.name, params.symbol); } // Private helper: Route main method calls function _routeMainMethod(method, params) { ${features.includes("pausable") ? '// Check pause status for most operations\n if (method !== "unpause") {\n ${contractName}Pausable.requireNotPaused();\n }' : ''} // Route to appropriate handler if (method === "exampleMethod") { return _handleExampleMethod(params); } ${features.includes("ownable") ? 'else if (method === "transferOwnership") {\n ${contractName}Ownable.requireOwner();\n return ${contractName}Ownable.transferOwnership(params.newOwner);\n }' : ''} ${features.includes("pausable") ? 'else if (method === "pause") {\n ${contractName}Ownable.requireOwner();\n return ${contractName}Pausable.pause();\n } else if (method === "unpause") {\n ${contractName}Ownable.requireOwner();\n return ${contractName}Pausable.unpause();\n }' : ''} ${features.includes("whitelist") ? 'else if (method === "addToWhitelist") {\n ${contractName}Ownable.requireOwner();\n return ${contractName}Whitelist.addToWhitelist(params.address);\n } else if (method === "removeFromWhitelist") {\n ${contractName}Ownable.requireOwner();\n return ${contractName}Whitelist.removeFromWhitelist(params.address);\n }' : ''} ${features.includes("blacklist") ? 'else if (method === "addToBlacklist") {\n ${contractName}Ownable.requireOwner();\n return ${contractName}Blacklist.addToBlacklist(params.address);\n } else if (method === "removeFromBlacklist") {\n ${contractName}Ownable.requireOwner();\n return ${contractName}Blacklist.removeFromBlacklist(params.address);\n }' : ''} else { throw "Unknown method: " + method; } } // Private helper: Route query method calls function _routeQueryMethod(method, params) { var result = {}; if (method === "contractInfo") { result = ${contractName}Storage.loadJSON(${contractName}Storage.CONTRACT_INFO_KEY); } ${features.includes("ownable") ? 'else if (method === "owner") {\n result.owner = ${contractName}Ownable.owner();\n }' : ''} ${features.includes("pausable") ? 'else if (method === "isPaused") {\n result.isPaused = ${contractName}Pausable.isPaused();\n }' : ''} ${features.includes("whitelist") ? 'else if (method === "isWhitelisted") {\n result.isWhitelisted = ${contractName}Whitelist.isWhitelisted(params.address);\n }' : ''} ${features.includes("blacklist") ? 'else if (method === "isBlacklisted") {\n result.isBlacklisted = ${contractName}Blacklist.isBlacklisted(params.address);\n }' : ''} else { throw "Unknown query method: " + method; } return JSON.stringify(result); } // Private helper: Example business logic method function _handleExampleMethod(params) { // Validate inputs Utils.assert(params.value !== undefined, "Value parameter is required"); ${features.includes("ownable") ? '// Require owner for this operation\n ${contractName}Ownable.requireOwner();' : ''} ${features.includes("whitelist") ? '// Require whitelisted sender\n ${contractName}Whitelist.requireWhitelisted(Chain.msg.sender);' : ''} ${features.includes("blacklist") ? '// Require not blacklisted sender\n ${contractName}Blacklist.requireNotBlacklisted(Chain.msg.sender);' : ''} // Perform business logic // TODO: Implement your business logic here Chain.tlog("ExampleMethod", Chain.msg.sender, params.value); return true; } // ============================================================================ // PUBLIC CONTRACT ENTRY POINTS // ============================================================================ function init(input_str) { var input = JSON.parse(input_str); var params = input.params; _initializeContract(params); } function main(input_str) { var input = JSON.parse(input_str); var method = input.method; var params = input.params || {}; return _routeMainMethod(method, params); } function query(input_str) { var input = JSON.parse(input_str); var method = input.method; var params = input.params || {}; return _routeQueryMethod(method, params); } `; return code; } /** * Generate unit tests (offline) */ private generateUnitTests(options: ContractGenerationOptions, classes: ContractClass[]): string { const { contractName } = options; return `/** * ${contractName} - Unit Tests (Offline) * * These tests run without blockchain interaction for fast development. * They mock Chain and Utils objects to test contract logic. */ const assert = require('assert'); // Mock Chain object global.Chain = { msg: { sender: "ZTX_TEST_SENDER" }, block: { timestamp: Date.now() }, load: function(key) { return this._storage[key] || false; }, store: function(key, value) { this._storage[key] = value; }, tlog: function(...args) { this._logs.push(args); }, _storage: {}, _logs: [], _reset: function() { this._storage = {}; this._logs = []; } }; // Mock Utils object global.Utils = { addressCheck: (addr) => addr && addr.startsWith("ZTX"), stoI64Check: (val) => typeof val === 'string' && /^\\d+$/.test(val), int64Compare: (a, b) => BigInt(a) > BigInt(b) ? 1 : BigInt(a) < BigInt(b) ? -1 : 0, int64Add: (a, b) => (BigInt(a) + BigInt(b)).toString(), int64Sub: (a, b) => (BigInt(a) - BigInt(b)).toString(), assert: function(condition, message) { if (!condition) throw new Error(message); } }; // Load contract modules const ${contractName}Storage = require('../library/${contractName}Storage'); const ${contractName}Validation = require('../library/${contractName}Validation'); ${options.features?.includes("ownable") ? `const ${contractName}Ownable = require('../library/${contractName}Ownable');` : ''} ${options.features?.includes("pausable") ? `const ${contractName}Pausable = require('../library/${contractName}Pausable');` : ''} describe('${contractName} Unit Tests', function() { beforeEach(function() { Chain._reset(); }); describe('Storage Module', function() { it('should store and load JSON', function() { const testData = { name: "Test", value: "123" }; ${contractName}Storage.storeJSON("test_key", testData); const loaded = ${contractName}Storage.loadJSON("test_key"); assert.deepEqual(loaded, testData); }); }); describe('Validation Module', function() { it('should validate Zetrix addresses', function() { assert.doesNotThrow(() => { ${contractName}Validation.requireValidAddress("ZTX_VALID_ADDRESS", "testAddr"); }); }); it('should reject invalid addresses', function() { assert.throws(() => { ${contractName}Validation.requireValidAddress("INVALID", "testAddr"); }); }); it('should validate amounts', function() { assert.doesNotThrow(() => { ${contractName}Validation.requireValidAmount("1000", "testAmount"); }); }); it('should reject invalid amounts', function() { assert.throws(() => { ${contractName}Validation.requireValidAmount("-100", "testAmount"); }); }); }); ${options.features?.includes("ownable") ? ` describe('Ownable Module', function() { it('should set and get owner', function() { ${contractName}Ownable._setOwner("ZTX_OWNER_ADDRESS"); assert.equal(${contractName}Ownable.owner(), "ZTX_OWNER_ADDRESS"); }); it('should transfer ownership', function() { Chain.msg.sender = "ZTX_OWNER_ADDRESS"; ${contractName}Ownable._setOwner("ZTX_OWNER_ADDRESS"); ${contractName}Ownable.transferOwnership("ZTX_NEW_OWNER"); assert.equal(${contractName}Ownable.owner(), "ZTX_NEW_OWNER"); }); it('should reject non-owner calls', function() { ${contractName}Ownable._setOwner("ZTX_OWNER_ADDRESS"); Chain.msg.sender = "ZTX_OTHER_ADDRESS"; assert.throws(() => { ${contractName}Ownable.requireOwner(); }); }); }); ` : ''} ${options.features?.includes("pausable") ? ` describe('Pausable Module', function() { it('should pause and unpause', function() { ${contractName}Pausable.pause(); assert.equal(${contractName}Pausable.isPaused(), true); ${contractName}Pausable.unpause(); assert.equal(${contractName}Pausable.isPaused(), false); }); it('should throw when paused', function() { ${contractName}Pausable.pause(); assert.throws(() => { ${contractName}Pausable.requireNotPaused(); }); }); }); ` : ''} }); console.log('Running unit tests...'); // If using Mocha: run with \`npm test tests/unit/${contractName}Test.js\` `; } /** * Generate integration tests (on-chain) */ private generateIntegrationTests(options: ContractGenerationOptions): string { const { contractName, features = [] } = options; return `/** * ${contractName} - Integration Tests (On-Chain) * * These tests deploy and interact with the actual contract on Zetrix testnet. * Run with: npm test tests/integration/${contractName}IntegrationTest.js */ const { TEST_INVOKE, TEST_QUERY, TEST_CONFIG } = require('../../utils/test-helpers'); const CONTRACT_ADDRESS = ""; // Will be set after deployment const OWNER_ADDRESS = TEST_CONFIG.owner; const USER1_ADDRESS = TEST_CONFIG.user1; const USER2_ADDRESS = TEST_CONFIG.user2; describe('${contractName} Integration Tests', function() { this.timeout(30000); // Blockchain operations can take time describe('1. Contract Deployment', function() { it('should deploy successfully', async function() { // Deploy contract and get address // const address = await deployContract(); // CONTRACT_ADDRESS = address; }); }); describe('2. Initialization', function() { it('should initialize with valid parameters', async function() { await TEST_INVOKE({ contract: CONTRACT_ADDRESS, method: 'init', params: { name: '${contractName}', symbol: 'SYMBOL', ${features.includes("ownable") ? 'owner: OWNER_ADDRESS' : ''} }, sender: OWNER_ADDRESS, expected: { success: true, logs: ['ContractInitialized'] } }); }); it('should query contract info', async function() { await TEST_QUERY({ contract: CONTRACT_ADDRESS, method: 'contractInfo', expected: { success: true, result: { name: '${contractName}', symbol: 'SYMBOL', version: '1.0.0' } } }); }); }); ${features.includes("ownable") ? ` describe('3. Ownership Management', function() { it('should return current owner', async function() { await TEST_QUERY({ contract: CONTRACT_ADDRESS, method: 'owner', expected: { success: true, result: { owner: OWNER_ADDRESS }, condition: 'EQUALS' } }); }); it('should allow owner to transfer ownership', async function() { await TEST_INVOKE({ contract: CONTRACT_ADDRESS, method: 'transferOwnership', params: { newOwner: USER1_ADDRESS }, sender: OWNER_ADDRESS, expected: { success: true, logs: ['OwnershipTransferred'] } }); }); it('should reject non-owner transfer attempt', async function() { await TEST_INVOKE({ contract: CONTRACT_ADDRESS, method: 'transferOwnership', params: { newOwner: USER2_ADDRESS }, sender: USER2_ADDRESS, expected: { success: false, errorContains: 'only owner' } }); }); }); ` : ''} ${features.includes("pausable") ? ` describe('4. Pause Functionality', function() { it('should allow owner to pause', async function() { await TEST_INVOKE({ contract: CONTRACT_ADDRESS, method: 'pause', sender: OWNER_ADDRESS, expected: { success: true, logs: ['Paused'] } }); }); it('should show paused status', async function() { await TEST_QUERY({ contract: CONTRACT_ADDRESS, method: 'isPaused', expected: { success: true, result: { isPaused: true } } }); }); it('should block operations when paused', async function() { await TEST_INVOKE({ contract: CONTRACT_ADDRESS, method: 'exampleMethod', params: { value: 'test' }, sender: OWNER_ADDRESS, expected: { success: false, errorContains: 'paused' } }); }); it('should allow owner to unpause', async function() { await TEST_INVOKE({ contract: CONTRACT_ADDRESS, method: 'unpause', sender: OWNER_ADDRESS, expected: { success: true, logs: ['Unpaused'] } }); }); }); ` : ''} ${features.includes("whitelist") ? ` describe('5. Whitelist Management', function() { it('should add address to whitelist', async function() { await TEST_INVOKE({ contract: CONTRACT_ADDRESS, method: 'addToWhitelist', params: { address: USER1_ADDRESS }, sender: OWNER_ADDRESS, expected: { success: true, logs: ['Whitelisted'] } }); }); it('should verify whitelisted status', async function() { await TEST_QUERY({ contract: CONTRACT_ADDRESS, method: 'isWhitelisted', params: { address: USER1_ADDRESS }, expected: { success: true, result: { isWhitelisted: true } } }); }); it('should remove address from whitelist', async function() { await TEST_INVOKE({ contract: CONTRACT_ADDRESS, method: 'removeFromWhitelist', params: { address: USER1_ADDRESS }, sender: OWNER_ADDRESS, expected: { success: true, logs: ['RemovedFromWhitelist'] } }); }); }); ` : ''} describe('99. Security & Edge Cases', function() { it('should reject invalid address formats', async function() { await TEST_INVOKE({ contract: CONTRACT_ADDRESS, method: 'exampleMethod', params: { address: 'INVALID_ADDRESS' }, sender: OWNER_ADDRESS, expected: { success: false, errorContains: 'valid address' } }); }); it('should handle concurrent operations', async function() { // Test race conditions and state consistency }); }); }); `; } /** * Generate deployment script */ private generateDeploymentScript(options: ContractGenerationOptions): string { const { contractName } = options; return `/** * Deployment script for ${contractName} * * Usage: * node scripts/deploy.js --network testnet * node scripts/deploy.js --network mainnet */ require('dotenv').config(); const { deployContract, verifyDeployment } = require('../utils/deploy-helpers'); async function deploy() { const network = process.argv.includes('--network') ? process.argv[process.argv.indexOf('--network') + 1] : 'testnet'; console.log(\`Deploying ${contractName} to \${network}...\`); // Contract initialization parameters const initParams = { name: '${contractName}', symbol: 'SYMBOL', // TODO: Change to your token symbol ${options.features?.includes("ownable") ? 'owner: process.env.ZTX_ADDRESS,' : ''} // Add other initialization parameters here }; try { // Deploy contract const result = await deployContract({ contractPath: \`./contracts/specs/${contractName}.js\`, initParams, network, gasLimit: '10000000', gasPrice: '5' }); console.log('✅ Deployment successful!'); console.log(\`Contract Address: \${result.address}\`); console.log(\`Transaction Hash: \${result.txHash}\`); // Verify deployment const verified = await verifyDeployment(result.address, network); if (verified) { console.log('✅ Deployment verified on blockchain'); } // Save deployment info const fs = require('fs'); const deploymentInfo = { network, address: result.address, txHash: result.txHash, timestamp: new Date().toISOString(), initParams }; fs.writeFileSync( \`./deployments/\${contractName}-\${network}.json\`, JSON.stringify(deploymentInfo, null, 2) ); console.log(\`\\nDeployment info saved to ./deployments/\${contractName}-\${network}.json\`); } catch (error) { console.error('❌ Deployment failed:', error.message); process.exit(1); } } deploy(); `; } /** * Generate project structure and files */ public generate(options: ContractGenerationOptions): { files: { path: string; content: string; type: string }[]; summary: string; classDiagram: string; } { const { contractName, outputDirectory = "." } = options; // Analyze requirements and generate class structure const classes = this.analyzeAndGenerateClasses(options); // Generate class diagram const classDiagram = this.generateClassDiagram(classes); const files: { path: string; content: string; type: string }[] = []; // Generate interface (optional, for documentation) const interfaceClass = classes.find(c => c.type === "interface"); if (interfaceClass) { files.push({ path: join(outputDirectory, `contracts/interface/I${contractName}.md`), content: `# ${interfaceClass.name} Interface\n\n${classDiagram}`, type: "interface" }); } // Generate libraries const libraries = classes.filter(c => c.type === "library"); for (const lib of libraries) { files.push({ path: join(outputDirectory, `contracts/library/${lib.name}.js`), content: this.generateLibrary(lib.name, lib), type: "library" }); } // Generate main contract (MUST be in contracts/ root, NOT specs/) files.push({ path: join(outputDirectory, `contracts/${contractName}.js`), content: this.generateMainContract(options, classes), type: "contract" }); // Generate tests if (options.includeTests !== false) { files.push({ path: join(outputDirectory, `tests/unit/${contractName}Test.js`), content: this.generateUnitTests(options, classes), type: "test-unit" }); files.push({ path: join(outputDirectory, `tests/integration/${contractName}IntegrationTest.js`), content: this.generateIntegrationTests(options), type: "test-integration" }); } // Generate deployment script files.push({ path: join(outputDirectory, `scripts/deploy-${contractName}.js`), content: this.generateDeploymentScript(options), type: "deployment" }); // Generate class diagram as separate file files.push({ path: join(outputDirectory, `docs/${contractName}-Architecture.md`), content: classDiagram, type: "documentation" }); const summary = `Successfully generated ${contractName} contract architecture! 📊 Class Structure Analysis: - ${classes.length} classes identified - ${classes.filter(c => c.type === "library").length} library modules - ${classes.filter(c => c.type === "interface").length} interface definition - ${classes.filter(c => c.type === "contract").length} main contract 📁 Generated Files (${files.length} total): ${files.map(f => { const emoji = f.type === "interface" ? "📋" : f.type === "library" ? "📚" : f.type === "contract" ? "📜" : f.type === "test-unit" ? "🧪" : f.type === "test-integration" ? "🔗" : f.type === "deployment" ? "🚀" : "📖"; return `${emoji} ${f.path}`; }).join('\n')} ✨ Key Features: - ES5-compatible JavaScript (Zetrix VM ready) - Private methods defined before public methods - Modular architecture with separate libraries - Comprehensive test coverage (unit + integration) - Production-ready deployment scripts - Full class diagram documentation 🔧 Features Implemented: ${options.features?.join(", ") || "Basic functionality"} 📝 Next Steps: 1. Review the class diagram in docs/${contractName}-Architecture.md 2. Customize business logic in contracts/specs/${contractName}.js 3. Run unit tests: npm test tests/unit/${contractName}Test.js 4. Run integration tests: npm test tests/integration/${contractName}IntegrationTest.js 5. Deploy: node scripts/deploy-${contractName}.js --network testnet `; return { files, summary, classDiagram }; } public writeFiles(files: { path: string; content: string }[]): void { for (const file of files) { const dir = file.path.substring(0, file.path.lastIndexOf("/")); if (dir && !existsSync(dir)) { mkdirSync(dir, { recursive: true }); } writeFileSync(file.path, file.content, "utf-8"); } } }

Implementation Reference

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Zetrix-Chain/zetrix-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server