@openzeppelin/contracts-ui-builder-adapter-stellar
Version:
Stellar Adapter for Contracts UI Builder
171 lines (155 loc) • 5.77 kB
text/typescript
import { xdr } from '@stellar/stellar-sdk';
import { startCase } from 'lodash';
import type {
ContractSchema,
FieldType,
FieldValidation,
FieldValue,
FormFieldType,
FunctionParameter,
} from '@openzeppelin/contracts-ui-builder-types';
import { getDefaultValueForType, logger } from '@openzeppelin/contracts-ui-builder-utils';
import { extractMapTypes, extractVecElementType } from '../utils/safe-type-parser';
import { isLikelyEnumType } from '../utils/type-detection';
import { extractEnumVariants, isEnumType, type EnumMetadata } from './enum-metadata';
import { mapStellarParameterTypeToFieldType } from './type-mapper';
/**
* Get default validation rules for a parameter type.
* Only includes serializable validation rules - no custom functions.
*/
function getDefaultValidationForType(): FieldValidation {
return { required: true };
}
/**
* Generate default field configuration for a Stellar function parameter.
*/
export function generateStellarDefaultField<T extends FieldType = FieldType>(
parameter: FunctionParameter,
contractSchema?: ContractSchema
): FormFieldType<T> {
// Extract spec entries from contract schema metadata if available
const specEntries = contractSchema?.metadata?.specEntries as xdr.ScSpecEntry[] | undefined;
const fieldType = mapStellarParameterTypeToFieldType(parameter.type) as T;
// Debug logging for unmapped types
if (parameter.type === 'unknown') {
logger.warn(
'adapter-stellar',
`[generateStellarDefaultField] Parameter "${parameter.name}" has type "unknown"`
);
}
let enumMetadata: EnumMetadata | null = null;
let finalFieldType = fieldType;
let options: { label: string; value: string }[] | undefined;
// Check if this parameter is an enum type and extract metadata
if (isLikelyEnumType(parameter.type)) {
if (specEntries && isEnumType(specEntries, parameter.type)) {
// We have spec entries, extract full metadata
enumMetadata = extractEnumVariants(specEntries, parameter.type);
if (enumMetadata) {
if (enumMetadata.isUnitOnly) {
// Unit-only enums can use select/radio with options
finalFieldType = 'select' as T;
options = enumMetadata.variants.map((variant) => ({
label: variant.name,
value: variant.type === 'integer' ? variant.value!.toString() : variant.name,
}));
} else {
// Tagged enums with payloads use the composite enum field
finalFieldType = 'enum' as T;
}
}
} else {
// No spec entries available, but type looks like enum - use enum field as fallback
finalFieldType = 'enum' as T;
// Create minimal enum metadata for fallback
enumMetadata = {
name: parameter.type,
variants: [], // Empty variants will trigger fallback UI
isUnitOnly: false,
};
}
}
const baseField: FormFieldType<T> = {
id: `field-${Math.random().toString(36).substring(2, 9)}`,
name: parameter.name || parameter.type, // Use type if name missing
label: startCase(parameter.displayName || parameter.name || parameter.type),
type: finalFieldType,
placeholder: enumMetadata
? `Select ${parameter.displayName || parameter.name || parameter.type}`
: `Enter ${parameter.displayName || parameter.name || parameter.type}`,
helperText: parameter.description || '',
defaultValue: getDefaultValueForType(finalFieldType) as FieldValue<T>,
validation: getDefaultValidationForType(),
width: 'full',
options,
};
// For array types, provide element type information
if (fieldType === 'array') {
const elementType = extractVecElementType(parameter.type);
if (elementType) {
const elementFieldType = mapStellarParameterTypeToFieldType(elementType);
// Add array-specific properties
const arrayField = {
...baseField,
elementType: elementFieldType,
elementFieldConfig: {
type: elementFieldType,
validation: { required: true },
placeholder: `Enter ${elementType}`,
},
};
return arrayField;
}
}
// For map types, provide key and value type information
if (fieldType === ('map' as FieldType)) {
const mapTypes = extractMapTypes(parameter.type);
if (mapTypes) {
const keyFieldType = mapStellarParameterTypeToFieldType(mapTypes.keyType);
const valueFieldType = mapStellarParameterTypeToFieldType(mapTypes.valueType);
// Add map-specific properties
const mapField = {
...baseField,
mapMetadata: {
keyType: keyFieldType,
valueType: valueFieldType,
keyFieldConfig: {
type: keyFieldType,
validation: { required: true },
placeholder: `Enter ${mapTypes.keyType}`,
originalParameterType: mapTypes.keyType,
},
valueFieldConfig: {
type: valueFieldType,
validation: { required: true },
placeholder: `Enter ${mapTypes.valueType}`,
originalParameterType: mapTypes.valueType,
},
},
validation: {
...getDefaultValidationForType(),
min: 0,
// No max limit - users can add as many map entries as needed
},
};
return mapField;
}
}
// Preserve components for object and array-object types
if (parameter.components && (fieldType === 'object' || fieldType === 'array-object')) {
const result = {
...baseField,
components: parameter.components,
};
return result;
}
// Add enum metadata if available
if (enumMetadata) {
const result = {
...baseField,
enumMetadata,
};
return result;
}
return baseField;
}