@openzeppelin/contracts-ui-builder-adapter-stellar
Version:
Stellar Adapter for Contracts UI Builder
160 lines (148 loc) • 6.23 kB
text/typescript
import * as StellarSdk from '@stellar/stellar-sdk';
import { isDevelopmentOrTestEnvironment, logger } from '@openzeppelin/contracts-ui-builder-utils';
/**
* Utility functions for detecting and analyzing Stellar/Soroban parameter types
*/
/**
* Extract human-readable Soroban type name from ScSpecTypeDef
* Based on the Stellar SDK type system
*/
export function extractSorobanTypeFromScSpec(scSpecType: StellarSdk.xdr.ScSpecTypeDef): string {
try {
const typeSwitch = scSpecType.switch();
switch (typeSwitch) {
case StellarSdk.xdr.ScSpecType.scSpecTypeVal():
return 'Val';
case StellarSdk.xdr.ScSpecType.scSpecTypeBool():
return 'Bool';
case StellarSdk.xdr.ScSpecType.scSpecTypeVoid():
return 'Void';
case StellarSdk.xdr.ScSpecType.scSpecTypeError():
return 'Error';
case StellarSdk.xdr.ScSpecType.scSpecTypeU32():
return 'U32';
case StellarSdk.xdr.ScSpecType.scSpecTypeI32():
return 'I32';
case StellarSdk.xdr.ScSpecType.scSpecTypeU64():
return 'U64';
case StellarSdk.xdr.ScSpecType.scSpecTypeI64():
return 'I64';
case StellarSdk.xdr.ScSpecType.scSpecTypeTimepoint():
return 'Timepoint';
case StellarSdk.xdr.ScSpecType.scSpecTypeDuration():
return 'Duration';
case StellarSdk.xdr.ScSpecType.scSpecTypeU128():
return 'U128';
case StellarSdk.xdr.ScSpecType.scSpecTypeI128():
return 'I128';
case StellarSdk.xdr.ScSpecType.scSpecTypeU256():
return 'U256';
case StellarSdk.xdr.ScSpecType.scSpecTypeI256():
return 'I256';
case StellarSdk.xdr.ScSpecType.scSpecTypeBytes():
return 'Bytes';
case StellarSdk.xdr.ScSpecType.scSpecTypeBytesN(): {
const bytesNType = scSpecType.bytesN();
const size = bytesNType.n();
return `BytesN<${size}>`;
}
case StellarSdk.xdr.ScSpecType.scSpecTypeString():
return 'ScString';
case StellarSdk.xdr.ScSpecType.scSpecTypeSymbol():
return 'ScSymbol';
case StellarSdk.xdr.ScSpecType.scSpecTypeVec(): {
const vecType = scSpecType.vec();
const elementType = extractSorobanTypeFromScSpec(vecType.elementType());
return `Vec<${elementType}>`;
}
case StellarSdk.xdr.ScSpecType.scSpecTypeMap(): {
const mapType = scSpecType.map();
const keyType = extractSorobanTypeFromScSpec(mapType.keyType());
const valueType = extractSorobanTypeFromScSpec(mapType.valueType());
return `Map<${keyType}, ${valueType}>`;
}
case StellarSdk.xdr.ScSpecType.scSpecTypeTuple(): {
const tupleType = scSpecType.tuple();
const valueTypes = tupleType.valueTypes();
const typeNames = valueTypes.map((t) => extractSorobanTypeFromScSpec(t));
return `Tuple<${typeNames.join(', ')}>`;
}
case StellarSdk.xdr.ScSpecType.scSpecTypeOption(): {
const optionType = scSpecType.option();
const valueType = extractSorobanTypeFromScSpec(optionType.valueType());
return `Option<${valueType}>`;
}
case StellarSdk.xdr.ScSpecType.scSpecTypeResult(): {
const resultType = scSpecType.result();
const okType = extractSorobanTypeFromScSpec(resultType.okType());
const errorType = extractSorobanTypeFromScSpec(resultType.errorType());
return `Result<${okType}, ${errorType}>`;
}
case StellarSdk.xdr.ScSpecType.scSpecTypeAddress():
return 'Address';
case StellarSdk.xdr.ScSpecType.scSpecTypeMuxedAddress():
return 'MuxedAddress';
case StellarSdk.xdr.ScSpecType.scSpecTypeUdt(): {
const udtType = scSpecType.udt();
return udtType.name().toString();
}
default:
// COMPREHENSIVE TYPE MISS DETECTION
logger.error('extractSorobanTypeFromScSpec', `🚨 MISSING SCSPEC TYPE HANDLER 🚨`, {
typeSwitchValue: typeSwitch.value,
typeSwitchName: typeSwitch.name,
rawScSpecType: scSpecType,
message: 'This indicates a missing case in extractSorobanTypeFromScSpec switch statement',
actionRequired: 'Add support for this ScSpec type immediately',
sdkVersion: process.env.npm_package_dependencies_stellar_sdk || 'unknown',
});
// Create detailed error report for unknown types
const errorReport = {
type: 'MISSING_SCSPEC_TYPE',
scSpecType: typeSwitch.name,
value: typeSwitch.value,
timestamp: new Date().toISOString(),
};
// In development, throw an error to fail fast
if (isDevelopmentOrTestEnvironment()) {
throw new Error(
`Missing ScSpec type handler: ${typeSwitch.name} (value: ${typeSwitch.value}). Please add support for this type.`
);
}
// In production, log extensively but don't break
logger.error('STELLAR_ADAPTER_MISSING_TYPE', 'Missing ScSpec type handler:', errorReport);
return 'unknown';
}
} catch (error) {
logger.error('extractSorobanTypeFromScSpec', 'Failed to extract type:', error);
return 'unknown';
}
}
/**
* Determines if a parameter type is likely an enum based on naming conventions
* and common patterns in Stellar/Soroban contracts.
*
* @param parameterType The parameter type string to analyze
* @returns true if the type appears to be an enum based on naming patterns
*/
export function isLikelyEnumType(parameterType: string): boolean {
// Direct enum name patterns
if (parameterType.includes('Enum') || parameterType.includes('enum')) {
return true;
}
// Common enum naming patterns in Stellar/Soroban
const enumPatterns = [
/^(Status|State|Type|Kind|Mode|Level|Priority|Category)$/i,
/^.*?(Status|State|Type|Kind|Mode|Level|Priority|Category)$/i,
/^(Token|Asset|Account|Contract|Network)Type$/i,
];
// Only apply pattern matching if it's not a generic "UnknownType" or similar
if (
parameterType === 'UnknownType' ||
parameterType === 'CustomStruct' ||
parameterType === 'UserInfo'
) {
return false;
}
return enumPatterns.some((pattern) => pattern.test(parameterType));
}