UNPKG

@openzeppelin/contracts-ui-builder-adapter-stellar

Version:
200 lines (174 loc) 6.19 kB
import type { FieldType } from '@openzeppelin/contracts-ui-builder-types'; import { isLikelyEnumType } from '../utils/type-detection'; import { STELLAR_TYPE_TO_FIELD_TYPE } from './constants'; /** * Map a Stellar-specific parameter type to a default form field type. * @param parameterType The Stellar parameter type (e.g., 'U128', 'Address', 'Vec<U32>') * @returns The appropriate default form field type (e.g., 'number', 'blockchain-address', 'array') */ export function mapStellarParameterTypeToFieldType(parameterType: string): FieldType { // Check if this is a Vec of custom/complex types (e.g., Vec<CustomStruct>) const vecComplexMatch = parameterType.match(/^Vec<([^>]+)>$/); if (vecComplexMatch) { const innerType = vecComplexMatch[1]; // If inner type is not a primitive, treat as array of objects if (!STELLAR_TYPE_TO_FIELD_TYPE[innerType]) { return 'array-object'; } return 'array'; } // Check if this is a simple Vec type if (parameterType === 'Vec' || parameterType.startsWith('Vec<')) { return 'array'; } // Check if this is a Map type if (parameterType === 'Map' || parameterType.startsWith('Map<')) { return 'map' as FieldType; } // Extract base type for generic types (e.g., Option<U32> -> U32) const genericMatch = parameterType.match(/^(\w+)<(.+)>$/); if (genericMatch) { const baseType = genericMatch[1]; // For Option and Result types, use the inner type if (baseType === 'Option' || baseType === 'Result') { const innerType = genericMatch[2]; return mapStellarParameterTypeToFieldType(innerType); } } // Map known Stellar/Soroban types to appropriate field types const mappedType = STELLAR_TYPE_TO_FIELD_TYPE[parameterType]; if (mappedType) { return mappedType; } // Handle BytesN types (fixed-size byte arrays like BytesN<32> for hashes) if (parameterType.startsWith('BytesN<')) { return 'textarea'; // Users input as hex strings } // Handle custom types (structs, enums) - default to object unless it's clearly an enum if (isLikelyEnumType(parameterType)) { return 'select'; } // Check if this looks like a custom struct (capitalized name and not a known primitive) if (parameterType[0] && parameterType[0] === parameterType[0].toUpperCase()) { // Check if it's not a known Stellar primitive type that starts with uppercase const knownUppercaseTypes = [ 'U32', 'U64', 'U128', 'U256', 'I32', 'I64', 'I128', 'I256', 'Bool', 'Bytes', ]; if ( !knownUppercaseTypes.includes(parameterType) && !parameterType.startsWith('Vec') && !parameterType.startsWith('Map') && !parameterType.includes('Unknown') ) { // Don't treat Unknown* as structs return 'object'; } } // Final fallback to text for truly unknown types return 'text'; } /** * Get field types compatible with a specific parameter type. * @param parameterType The Stellar parameter type. * @returns Array of compatible form field types. */ export function getStellarCompatibleFieldTypes(parameterType: string): FieldType[] { // Handle Vec of complex types const vecComplexMatch = parameterType.match(/^Vec<([^>]+)>$/); if (vecComplexMatch) { const innerType = vecComplexMatch[1]; if (!STELLAR_TYPE_TO_FIELD_TYPE[innerType]) { return ['array-object', 'textarea', 'text']; } return ['array', 'textarea', 'text']; } // Handle Vec types - allow array field or fallback to textarea/text if (parameterType === 'Vec' || parameterType.startsWith('Vec<')) { return ['array', 'textarea', 'text']; } // Handle Map types if (parameterType === 'Map' || parameterType.startsWith('Map<')) { return ['map' as FieldType, 'textarea', 'text']; } // Handle generic types const genericMatch = parameterType.match(/^(\w+)<(.+)>$/); if (genericMatch) { const baseType = genericMatch[1]; if (baseType === 'Option' || baseType === 'Result') { const innerType = genericMatch[2]; return getStellarCompatibleFieldTypes(innerType); } } // Define compatibility map for known types const compatibilityMap: Record<string, FieldType[]> = { Address: ['blockchain-address', 'text'], // Unsigned integers U32: ['number', 'amount', 'text'], U64: ['number', 'amount', 'text'], U128: ['number', 'amount', 'text'], U256: ['number', 'amount', 'text'], // Signed integers I32: ['number', 'amount', 'text'], I64: ['number', 'amount', 'text'], I128: ['number', 'amount', 'text'], I256: ['number', 'amount', 'text'], // Boolean Bool: ['checkbox', 'select', 'radio', 'text'], // String types ScString: ['text', 'textarea', 'email', 'password'], ScSymbol: ['text', 'textarea'], // Byte types Bytes: ['bytes', 'textarea', 'text'], DataUrl: ['bytes', 'textarea', 'text'], // BytesN types like BytesN<32> for hashes 'BytesN<32>': ['bytes', 'textarea', 'text'], // Complex types Tuple: ['object', 'textarea', 'text'], Instance: ['object', 'textarea', 'text'], }; // Check if we have a specific compatibility mapping const compatibleTypes = compatibilityMap[parameterType]; if (compatibleTypes) { return compatibleTypes; } // Handle enums if (isLikelyEnumType(parameterType)) { return ['enum', 'select', 'radio', 'text']; } // Handle custom types (assumed to be structs) if (parameterType[0] && parameterType[0] === parameterType[0].toUpperCase()) { // Check if it's not a known Stellar primitive type that starts with uppercase const knownUppercaseTypes = [ 'U32', 'U64', 'U128', 'U256', 'I32', 'I64', 'I128', 'I256', 'Bool', 'Bytes', ]; if ( !knownUppercaseTypes.includes(parameterType) && !parameterType.startsWith('Vec') && !parameterType.startsWith('Map') && !parameterType.includes('Unknown') ) { // Don't treat Unknown* as structs return ['object', 'textarea', 'text']; } } // Default fallback for truly unknown types return ['text']; }