UNPKG

navskit

Version:

Deploy TypeScript logic on Ethereum. Includes core library, CLI tools, and utilities.

787 lines (755 loc) 35.6 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.NavsGenerator = void 0; const fs = __importStar(require("fs")); const path = __importStar(require("path")); const deploy_1 = require("../../deploy"); const viem_1 = require("viem"); class NavsGenerator { targetDir; isLocal; constructor(targetDir, isLocal = false) { this.targetDir = path.resolve(targetDir); this.isLocal = isLocal; } async generate() { const { packageJson } = this.validateProjectStructure(); const functions = this.extractNavsFunctions(); if (functions.length === 0) { console.log('⚠️ No @navs functions found - skipping Solidity contract generation'); return; } this.generateSolidityLibrary(functions, packageJson); } validateProjectStructure() { const packageJsonPath = path.join(this.targetDir, 'package.json'); if (!fs.existsSync(packageJsonPath)) { throw new Error('No package.json found in target directory'); } const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); const main = packageJson.main || 'index.js'; return { main, packageJson }; } extractNavsFunctions() { return this.performStaticAnalysis(); } performStaticAnalysis() { // Read the source files and look for @navs decorators const srcDir = path.join(this.targetDir, 'src'); const functions = []; if (fs.existsSync(srcDir)) { const files = this.findTypeScriptFiles(srcDir); for (const file of files) { const content = fs.readFileSync(file, 'utf8'); // Check if file contains @navs if (content.includes('@navs')) { const extractedFunctions = this.extractFunctionsFromFile(content, file); functions.push(...extractedFunctions); } } } console.log(`📄 Found ${functions.length} @navs functions`); return functions; } findTypeScriptFiles(dir) { const files = []; function walkDir(currentDir) { const items = fs.readdirSync(currentDir); for (const item of items) { const fullPath = path.join(currentDir, item); const stat = fs.statSync(fullPath); if (stat.isDirectory() && item !== 'node_modules' && item !== 'dist') { walkDir(fullPath); } else if (stat.isFile() && item.endsWith('.ts')) { files.push(fullPath); } } } walkDir(dir); return files; } extractFunctionsFromFile(content, filename) { const functions = []; // Improved regex to find @navs decorated functions // This matches: @navs(optional_params) static/async? functionName(params): ReturnType const navsRegex = /@navs\s*(\([^)]*\))?\s*(?:static\s+)?(?:async\s+)?(\w+)\s*\([^)]*\)\s*:\s*(?:Promise<([^>]+)>|([^\s{]+))/g; let match; while ((match = navsRegex.exec(content)) !== null) { const navsParams = match[1]; // The parameters inside @navs(...) const functionName = match[2]; const promiseReturnType = match[3]; // From Promise<Type> const directReturnType = match[4]; // Direct return type const returnType = promiseReturnType || directReturnType || 'unknown'; // Determine consensus type based on @navs parameters const consensusType = this.determineConsensusType(navsParams); if (functionName) { functions.push({ name: functionName, parameters: this.extractParametersFromFunction(content, functionName), returnType: this.mapTypeScriptToSolidity(returnType), description: `Function ${functionName} from ${path.basename(filename)}`, consensusType }); } } // Also try a simpler approach - look for lines with @navs followed by function declarations const lines = content.split('\n'); for (let i = 0; i < lines.length - 1; i++) { const line = lines[i].trim(); if (line.includes('@navs')) { const navsMatch = line.match(/@navs\s*(\([^)]*\))?/); const navsParams = navsMatch ? navsMatch[1] : null; const consensusType = this.determineConsensusType(navsParams); const nextLine = lines[i + 1]; const funcMatch = nextLine.match(/(?:static\s+)?(?:async\s+)?(\w+)\s*\([^)]*\)\s*:\s*(?:Promise<([^>]+)>|([^\s{]+))/); if (funcMatch) { const functionName = funcMatch[1]; const promiseReturnType = funcMatch[2]; const directReturnType = funcMatch[3]; const returnType = promiseReturnType || directReturnType || 'unknown'; // Check if we already found this function if (!functions.some(f => f.name === functionName)) { functions.push({ name: functionName, parameters: this.extractParametersFromFunction(content, functionName), returnType: this.mapTypeScriptToSolidity(returnType), description: `Function ${functionName} from ${path.basename(filename)}`, consensusType }); } } } } return functions; } determineConsensusType(navsParams) { // If @navs() has no parameters, use EXACT_MATCH if (!navsParams || navsParams === '()') { return 'EXACT_MATCH'; } // If @navs has parameters (like consensus.numericalAverage), use CUSTOM return 'CUSTOM'; } extractParametersFromFunction(content, functionName) { // Try to extract parameters from function signature, being more specific to avoid comments // Look for the pattern that starts a line (or after whitespace) with static/async keywords const funcRegex = new RegExp(`(?:^|\\n)\\s*(?:static\\s+)?(?:async\\s+)?${functionName}\\s*\\(([^)]*)\\)\\s*:`, 'm'); const match = funcRegex.exec(content); if (match && match[1]) { const paramString = match[1].trim(); if (paramString) { const params = paramString.split(',').map(p => p.trim()); return params.map((param, index) => { const colonIndex = param.indexOf(':'); if (colonIndex > 0) { const name = param.substring(0, colonIndex).trim(); const type = param.substring(colonIndex + 1).trim(); const safeName = this.getSafeParameterName(name || `param${index + 1}`); return { name: safeName, type: this.mapTypeScriptToSolidity(type) }; } else { return { name: this.getSafeParameterName(param || `param${index + 1}`), type: 'bytes' }; } }); } } return []; } getSafeParameterName(name) { // Solidity reserved keywords that we need to avoid const reservedKeywords = [ 'address', 'bool', 'string', 'bytes', 'uint256', 'int256', 'function', 'contract', 'library', 'interface', 'struct', 'enum', 'mapping', 'array', 'modifier', 'event', 'using', 'import', 'pragma', 'assembly', 'memory', 'storage', 'calldata', 'pure', 'view', 'payable', 'nonpayable', 'external', 'internal', 'public', 'private' ]; if (reservedKeywords.includes(name.toLowerCase())) { return `${name}_param`; } return name; } addDataLocationForInternal(type) { if (type === 'string' || type === 'bytes' || type.endsWith('[]')) { return `${type} memory`; } return type; } addDataLocation(type) { // Add appropriate data location for reference types in external functions if (type === 'string' || type === 'bytes' || type.endsWith('[]')) { return `${type} calldata`; } return type; } addDataLocationForAbiDecode(type) { // For abi.decode results, reference types should use memory if (type === 'string' || type === 'bytes' || type.endsWith('[]')) { return `${type} memory`; } return type; } generateSolidityLibrary(functions, packageJson) { const contractsDir = path.join(this.targetDir, 'contracts'); if (!fs.existsSync(contractsDir)) { fs.mkdirSync(contractsDir, { recursive: true }); } // Generate combined NavsReceiver abstract contract (includes service functions) const combinedSolidity = this.generateCombinedNavsReceiver(functions, packageJson); const receiverPath = path.join(contractsDir, 'NavsReceiver.sol'); fs.writeFileSync(receiverPath, combinedSolidity); console.log('✓ Generated contracts/NavsReceiver.sol'); } generateServiceLibrary(functions, packageJson) { const libraryName = this.generateLibraryName(packageJson.name); let solidityCode = `// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "navs/src/ITaskDispatch.sol"; import "navs/src/IBaseNavsReceiver.sol"; /** * Auto-generated Service Library for NAVS function calls * * This library provides asynchronous calls for functions decorated with navs annotations. * All functions submit tasks via TaskDispatch and receive results via callbacks. * * Generated by navs-gen on ${new Date().toISOString()} * Total functions: ${functions.length} */ library ${libraryName} { // Package information constants string internal constant PACKAGE_NAME = "${packageJson.name || 'unknown'}"; string internal constant PACKAGE_VERSION = "${packageJson.version || '1.0.0'}"; ${functions.length === 0 ? ' // No navs functions found in the project' : ''}`; // Generate async function implementations for (const func of functions) { solidityCode += this.generateAsyncFunction(func); } // Add utility functions solidityCode += this.generateUtilityFunctions(packageJson); solidityCode += '}\n'; return solidityCode; } generateCombinedNavsReceiver(functions, packageJson) { // Get TaskDispatch address for modifier const taskDispatchAddresses = this.getTaskDispatchAddresses(); const localWarning = this.isLocal ? ` /* ╔══════════════════════════════════════════════════════════════════════════════════════╗ ║ ⚠️ LOCAL MODE ⚠️ ║ ║ ║ ║ This contract was generated with --local flag for LOCAL DEVELOPMENT ONLY! ║ ║ ║ ║ 🏠 LOCAL FEATURES ENABLED: ║ ║ • Zero stake requirement (no economic cost) ║ ║ • Immediate task completion (no consensus waiting) ║ ║ • Uses file:// dependencies instead of npm packages ║ ║ • Only processes tasks matching current package directory ║ ║ ║ ║ 🚨 DO NOT USE IN PRODUCTION! This is for testing and development only. ║ ║ For production, regenerate without --local flag. ║ ╚══════════════════════════════════════════════════════════════════════════════════════╝ */ ` : ''; let combinedCode = `// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "navs/src/ITaskDispatch.sol"; import "navs/src/IBaseNavsReceiver.sol"; ${localWarning} /** * Abstract NavsReceiver contract that implements IBaseNavsReceiver * * This contract combines service functions and callback handling in one abstract contract. * Users should extend this contract and override the abstract callback methods they need. * This contract automatically handles onNavsResult and onNavsError routing from TaskDispatch. * * Generated by navs-gen on ${new Date().toISOString()} * Total functions: ${functions.length}${this.isLocal ? '\n * \n * 🏠 LOCAL MODE: Generated with --local flag for development only!' : ''} */ abstract contract NavsReceiver is IBaseNavsReceiver { // Package information constants string public constant PACKAGE_NAME = "${packageJson.name || 'unknown'}"; string public constant PACKAGE_VERSION = "${packageJson.version || '1.0.0'}"; ${taskDispatchAddresses} /** * Modifier to ensure only TaskDispatch can call callback functions */ modifier onlyTaskDispatch() { address taskDispatchAddr = getTaskDispatchAddress(); require(msg.sender == taskDispatchAddr, "Only TaskDispatch can call this function"); _; } /** * Implementation of IBaseNavsReceiver.onNavsResult * Automatically routes to typed callback methods * Can only be called by TaskDispatch contract */ function onNavsResult(uint256 taskId, string memory functionName, bytes memory result) external override onlyTaskDispatch { _handleNavsCallback(taskId, functionName, result, ""); } /** * Implementation of IBaseNavsReceiver.onNavsError * Automatically routes to typed callback methods with error message * Can only be called by TaskDispatch contract */ function onNavsError(uint256 taskId, string memory functionName, string memory error) external override onlyTaskDispatch { _handleNavsErrorCallback(taskId, functionName, error); } /** * Internal callback router for type-safe callbacks */ function _handleNavsCallback( uint256 taskId, string memory functionName, bytes memory result, string memory error ) internal { ${this.generateInternalCallbackRouter(functions)} revert("Unknown function"); } /** * Internal error callback router for type-safe error callbacks */ function _handleNavsErrorCallback( uint256 taskId, string memory functionName, string memory error ) internal { ${this.generateInternalErrorCallbackRouter(functions)} revert("Unknown function"); } // ================== SERVICE FUNCTIONS ==================${this.isLocal ? ` /* * 🏠 LOCAL MODE SERVICE FUNCTIONS * * These functions are configured for local development: * • requiredStakeWei parameter is ignored (always uses 0 stake) * • Tasks complete immediately without consensus * • Only operators monitoring this specific package will process tasks */` : ''} ${functions.length === 0 ? ' // No navs functions found in the project' : ''}`; // Generate async function implementations for (const func of functions) { combinedCode += this.generateAsyncFunction(func); } // Add utility functions (without getTaskDispatchAddress since it's already included above) combinedCode += this.generateUtilityFunctionsForContract(packageJson); // Generate abstract callback methods combinedCode += '\n // ================== ABSTRACT CALLBACK METHODS ==================\n'; combinedCode += functions.map(func => this.generateAbstractCallbackMethod(func)).join(''); combinedCode += '}\n'; return combinedCode; } generateNavsReceiverAbstract(functions, libraryName, packageJson) { // Get TaskDispatch address for modifier const taskDispatchAddresses = this.getTaskDispatchAddresses(); let abstractCode = `// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "navs/src/IBaseNavsReceiver.sol"; import "./service.sol"; /** * Abstract NavsReceiver contract that implements IBaseNavsReceiver * * Users should extend this contract and override the abstract callback methods they need. * This contract automatically handles onNavsResult and onNavsError routing from TaskDispatch. * * Generated by navs-gen on ${new Date().toISOString()} */ abstract contract NavsReceiver is IBaseNavsReceiver { using ${libraryName} for *; // Package information constants string public constant PACKAGE_NAME = "${packageJson.name || 'unknown'}"; string public constant PACKAGE_VERSION = "${packageJson.version || '1.0.0'}"; ${taskDispatchAddresses} /** * Modifier to ensure only TaskDispatch can call callback functions */ modifier onlyTaskDispatch() { address taskDispatchAddr = getTaskDispatchAddress(); require(msg.sender == taskDispatchAddr, "Only TaskDispatch can call this function"); _; } /** * Implementation of IBaseNavsReceiver.onNavsResult * Automatically routes to typed callback methods * Can only be called by TaskDispatch contract */ function onNavsResult(uint256 taskId, string memory functionName, bytes memory result) external override onlyTaskDispatch { _handleNavsCallback(taskId, functionName, result, ""); } /** * Implementation of IBaseNavsReceiver.onNavsError * Automatically routes to typed callback methods with error message * Can only be called by TaskDispatch contract */ function onNavsError(uint256 taskId, string memory functionName, string memory error) external override onlyTaskDispatch { _handleNavsErrorCallback(taskId, functionName, error); } /** * Internal callback router for type-safe callbacks */ function _handleNavsCallback( uint256 taskId, string memory functionName, bytes memory result, string memory error ) internal { ${this.generateInternalCallbackRouter(functions)} revert("Unknown function"); } /** * Internal error callback router for type-safe error callbacks */ function _handleNavsErrorCallback( uint256 taskId, string memory functionName, string memory error ) internal { ${this.generateInternalErrorCallbackRouter(functions)} revert("Unknown function"); } // Abstract callback methods - override these in your contract ${functions.map(func => this.generateAbstractCallbackMethod(func)).join('')} } `; return abstractCode; } generateAsyncFunction(func) { const params = func.parameters.map(p => `${this.addDataLocation(p.type)} ${p.name}`).join(', '); const paramNames = func.parameters.map(p => p.name); const hasParams = paramNames.length > 0; const allParams = hasParams ? `${params}, uint256 requiredStakeWei` : 'uint256 requiredStakeWei'; const paramDocs = func.parameters.length > 0 ? func.parameters.map(p => ` * @param ${p.name} Parameter of type ${p.type}`).join('\n') : ' * No parameters'; const localComment = this.isLocal ? '\n * 🏠 LOCAL MODE: requiredStakeWei is ignored, always uses 0 stake for local development' : ''; return ` /** * Call ${func.name} with callback * ${func.description || `Calls ${func.name} function via TaskDispatch and returns result via callback`} * The calling contract (address(this)) will receive the callback. ${paramDocs} * @param requiredStakeWei Minimum stake threshold for execution in wei${localComment} * @return taskId Unique identifier for tracking this task */ function ${func.name}(${allParams}) internal returns (uint256 taskId) { ${hasParams ? `bytes memory encodedParams = abi.encode(${paramNames.join(', ')});` : `bytes memory encodedParams = "";`} return _submitTaskWithCallback( "${func.name}", encodedParams, address(this), ${this.isLocal ? '0, // no stake used for local development' : 'requiredStakeWei'}, ITaskDispatch.ConsensusType.${func.consensusType}, ${this.isLocal} ); } `; } generateAbstractCallbackMethod(func) { const params = func.parameters.map(p => `${this.addDataLocationForInternal(p.type)} ${p.name}`).join(', '); const resultType = this.addDataLocationForInternal(func.returnType); const allParams = params ? `uint256 taskId, ${params}, ${resultType} result, string memory error` : `uint256 taskId, ${resultType} result, string memory error`; const paramDocs = func.parameters.length > 0 ? func.parameters.map(p => ` * @param ${p.name} Original parameter of type ${p.type}`).join('\n') : ''; return ` /** * Abstract callback for ${func.name} function completion * Override this function in your contract to handle ${func.name} results * @param taskId The unique identifier for the completed task ${paramDocs} * @param result The result returned by ${func.name} * @param error Error message if the function failed, empty string if successful */ function on${func.name.charAt(0).toUpperCase() + func.name.slice(1)}(${allParams}) internal virtual; `; } generateInternalCallbackRouter(functions) { let router = ''; for (const func of functions) { const paramNames = func.parameters.map(p => p.name); const hasParams = paramNames.length > 0; const capitalizedName = func.name.charAt(0).toUpperCase() + func.name.slice(1); router += ` if (keccak256(bytes(functionName)) == keccak256(bytes("${func.name}"))) {\n`; router += ` // Decode the enhanced callback data from TaskDispatch\n`; router += ` (bytes memory originalArgs, bytes memory packedResult) = abi.decode(result, (bytes, bytes));\n`; router += ` \n`; router += ` // Decode the packed result from navs-core\n`; router += ` (string memory resultType, bytes memory resultBytes, bytes32 l1calldatahash) = abi.decode(packedResult, (string, bytes, bytes32));\n`; router += ` \n`; if (hasParams) { // For functions with parameters, decode both original params and function result const paramTypes = func.parameters.map(p => p.type); const paramVarDeclarations = paramTypes.map((type, i) => `${this.addDataLocationForAbiDecode(type)} param${i}`); const paramVarNames = paramTypes.map((_, i) => `param${i}`); router += ` // Decode the original parameters\n`; router += ` (${paramVarDeclarations.join(', ')}) = abi.decode(originalArgs, (${paramTypes.join(', ')}));\n`; router += ` \n`; router += ` // Decode the function result\n`; router += ` ${this.addDataLocationForAbiDecode(func.returnType)} resultValue = abi.decode(resultBytes, (${func.returnType}));\n`; router += ` \n`; router += ` on${capitalizedName}(taskId, ${paramVarNames.join(', ')}, resultValue, error);\n`; } else { // For functions without parameters, just decode the function result router += ` // Decode the function result\n`; router += ` ${this.addDataLocationForAbiDecode(func.returnType)} resultValue = abi.decode(resultBytes, (${func.returnType}));\n`; router += ` \n`; router += ` on${capitalizedName}(taskId, resultValue, error);\n`; } router += ` return;\n`; router += ` }\n`; } return router; } generateInternalErrorCallbackRouter(functions) { let router = ''; for (const func of functions) { const capitalizedName = func.name.charAt(0).toUpperCase() + func.name.slice(1); const hasParams = func.parameters.length > 0; router += ` if (keccak256(bytes(functionName)) == keccak256(bytes("${func.name}"))) {\n`; if (hasParams) { const defaultParams = func.parameters.map(p => { return this.getDefaultValue(p.type); }).join(', '); const defaultReturnValue = this.getDefaultValue(func.returnType); router += ` on${capitalizedName}(taskId, ${defaultParams}, ${defaultReturnValue}, error);\n`; } else { const defaultReturnValue = this.getDefaultValue(func.returnType); router += ` on${capitalizedName}(taskId, ${defaultReturnValue}, error);\n`; } router += ` return;\n`; router += ` }\n`; } return router; } getDefaultValue(type) { // Handle array types first (before checking for uint/int) if (type.endsWith('[]')) { return `new ${type}(0)`; } if (type === 'string') return '""'; if (type === 'bytes') return 'bytes("")'; if (type.includes('uint') || type.includes('int')) return '0'; if (type === 'bool') return 'false'; if (type === 'address') return 'address(0)'; return 'bytes("")'; // fallback for complex types } generateUtilityFunctionsForContract(packageJson) { return ` /** * Internal function to handle asynchronous task submission with callback * @param functionName The name of the function to execute * @param encodedParams ABI-encoded parameters * @param callbackReceiver Address that will receive the callback * @param requiredStake Minimum stake threshold for execution * @param consensusType Type of consensus to use for the task * @param isLocal Whether this is a local task * @return taskId Unique identifier for tracking this task */ function _submitTaskWithCallback( string memory functionName, bytes memory encodedParams, address callbackReceiver, uint256 requiredStake, ITaskDispatch.ConsensusType consensusType, bool isLocal ) internal returns (uint256) { // Call TaskDispatch with callback receiver address taskDispatchAddr = getTaskDispatchAddress(); require(taskDispatchAddr != address(0), "TaskDispatch address not set for this chain"); uint256 taskId = ITaskDispatch(taskDispatchAddr).submitTask( "${packageJson.name || 'unknown-service'}", // Service name from package.json "${packageJson.version || '1.0.0'}", // Service version from package.json functionName, encodedParams, requiredStake, // Use the provided stake threshold consensusType, // Use the determined consensus type false, // isConsensus callbackReceiver, // Callback will be executed automatically by TaskDispatch isLocal, // Whether this is a local task true // isDeterministic - auto-mark generated tasks as deterministic ); return taskId; }`; } generateUtilityFunctions(packageJson) { // Get TaskDispatch addresses from navs-deploy const taskDispatchAddresses = this.getTaskDispatchAddresses(); return ` // TaskDispatch contract addresses from navs-deploy ${taskDispatchAddresses} /** * Internal function to handle asynchronous task submission with callback * @param functionName The name of the function to execute * @param encodedParams ABI-encoded parameters * @param callbackReceiver Address that will receive the callback * @param requiredStake Minimum stake threshold for execution * @param consensusType Type of consensus to use for the task * @param isLocal Whether this is a local task * @return taskId Unique identifier for tracking this task */ function _submitTaskWithCallback( string memory functionName, bytes memory encodedParams, address callbackReceiver, uint256 requiredStake, ITaskDispatch.ConsensusType consensusType, bool isLocal ) internal returns (uint256) { // Call TaskDispatch with callback receiver address taskDispatchAddr = getTaskDispatchAddress(); require(taskDispatchAddr != address(0), "TaskDispatch address not set for this chain"); uint256 taskId = ITaskDispatch(taskDispatchAddr).submitTask( "${packageJson.name || 'unknown-service'}", // Service name from package.json "${packageJson.version || '1.0.0'}", // Service version from package.json functionName, encodedParams, requiredStake, // Use the provided stake threshold consensusType, // Use the determined consensus type false, // isConsensus callbackReceiver, // Callback will be executed automatically by TaskDispatch isLocal, // Whether this is a local task true // isDeterministic - auto-mark generated tasks as deterministic ); return taskId; } `; } generateLibraryName(packageName) { // Remove invalid characters and convert to PascalCase const cleaned = packageName .replace(/[@/\-_]/g, ' ') // Replace special chars with spaces .replace(/[^a-zA-Z0-9\s]/g, '') // Remove any remaining invalid chars .split(' ') .filter(word => word.length > 0) .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()) .join(''); // Ensure it starts with a letter and is a valid Solidity identifier const result = cleaned.replace(/^[0-9]/, 'N$&'); // Prefix with 'N' if starts with number return result || 'NavsService'; // Fallback if empty } mapTypeScriptToSolidity(tsType) { // Handle Promise types if (tsType.startsWith('Promise<') && tsType.endsWith('>')) { const innerType = tsType.slice(8, -1); return this.mapTypeScriptToSolidity(innerType); } // Handle Ethereum address template literal types if (tsType === '`0x${string}`' || tsType === '0x${string}') { return 'address'; } // Basic type mappings const typeMap = { 'string': 'string', 'String': 'string', 'number': 'uint256', 'Number': 'uint256', 'boolean': 'bool', 'Boolean': 'bool', 'bigint': 'uint256', 'BigInt': 'uint256', 'undefined': 'bytes', 'unknown': 'bytes', 'any': 'bytes', 'void': 'bytes', 'object': 'bytes', 'Object': 'bytes' }; // Handle array types if (tsType.endsWith('[]')) { const elementType = tsType.slice(0, -2); const mappedElementType = this.mapTypeScriptToSolidity(elementType); return `${mappedElementType}[]`; } // Handle Array<T> syntax if (tsType.startsWith('Array<') && tsType.endsWith('>')) { const elementType = tsType.slice(6, -1); const mappedElementType = this.mapTypeScriptToSolidity(elementType); return `${mappedElementType}[]`; } return typeMap[tsType] || 'bytes'; } getTaskDispatchAddresses() { let addressesCode = ''; // Generate getter function for TaskDispatch address addressesCode += ` /** * Get TaskDispatch contract address for current chain * @return address TaskDispatch contract address (address(0) if not deployed on this chain) */ function getTaskDispatchAddress() internal view virtual returns (address) { uint256 chainId = block.chainid;`; // Add addresses from navs-deploy for (const [chainId, contracts] of Object.entries(deploy_1.Addresses)) { const contractsAny = contracts; if (contractsAny.TaskDispatch) { addressesCode += ` if (chainId == ${chainId}) return ${(0, viem_1.getAddress)(contractsAny.TaskDispatch)}; // ${this.getChainName(chainId)}`; } } addressesCode += ` return address(0); // TaskDispatch not deployed on this chain }`; return addressesCode; } getChainName(chainId) { const chainNames = { '1': 'Ethereum Mainnet', '11155111': 'Ethereum Sepolia', '84532': 'Base Sepolia', '8453': 'Base Mainnet' }; return chainNames[chainId] || `Chain ${chainId}`; } } exports.NavsGenerator = NavsGenerator;