UNPKG

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

Version:
158 lines (139 loc) 5.1 kB
import { nativeToScVal, xdr } from '@stellar/stellar-sdk'; import { detectBytesEncoding, stringToBytes } from '@openzeppelin/contracts-ui-builder-utils'; import { convertStellarTypeToScValType } from '../../utils/formatting'; import { convertEnumToScVal, convertObjectToMap, convertObjectToScVal, convertTupleToScVal, getScValFromArg, getScValFromPrimitive, isComplexObjectArray, isEnumArgumentSet, isMapArray, isObjectWithTypedValues, isPrimitiveArgumentSet, isPrimitiveArray, isPrimitiveValue, isTupleArray, } from '../../utils/input-parsing'; import type { SorobanArgumentValue, SorobanComplexValue, SorobanEnumValue, SorobanMapEntry, } from './types'; /** * Advanced ScVal generation from complex Soroban arguments. * Handles Maps, Enums, Tuples, and nested structures. * * @param args - Complex argument structure from advanced UI forms * @param scVals - Accumulator for generated ScVals (used for recursion) * @returns Array of ScVals ready for contract invocation */ export function getScValsFromArgs( args: Record<string, SorobanComplexValue> | SorobanComplexValue[], scVals: xdr.ScVal[] = [] ): xdr.ScVal[] { // Handle array input (multiple arguments) if (Array.isArray(args)) { return args.map((arg) => { if (typeof arg === 'object' && 'value' in arg && 'type' in arg) { return getScValFromPrimitive(arg as SorobanArgumentValue); } return getScValFromArg(arg, []); }); } // Handle primitive case - all values have type and value if (isPrimitiveArgumentSet(args)) { const primitiveScVals = Object.values(args).map((v) => { return getScValFromPrimitive(v as SorobanArgumentValue); }); return primitiveScVals; } // Handle enum case - values have tag or enum properties if (isEnumArgumentSet(args)) { const enumScVals = Object.values(args).map((v) => { return convertEnumToScVal(v as SorobanEnumValue, scVals); }); return enumScVals; } // Handle complex cases (maps, objects, arrays) for (const argKey in args) { const argValue = args[argKey]; if (Array.isArray(argValue)) { // Map case - array of key-value pair objects if (isMapArray(argValue)) { const { mapVal, mapType } = convertObjectToMap(argValue as SorobanMapEntry[]); const mapScVal = nativeToScVal(mapVal, { type: mapType }); scVals.push(mapScVal); continue; } // Vector case #1: array of complex objects or tuples if (isComplexObjectArray(argValue)) { const arrayScVals = argValue.map((v) => { // Use proper type guards to ensure safe casting if (typeof v === 'object' && v !== null && ('tag' in v || 'enum' in v)) { return convertEnumToScVal(v as SorobanEnumValue, scVals); } if ( typeof v === 'object' && v !== null && !Array.isArray(v) && !('0' in v) && !('1' in v) ) { return convertObjectToScVal(v as Record<string, SorobanArgumentValue>); } // Fallback for other types return nativeToScVal(v); }); const tupleScValsVec = xdr.ScVal.scvVec(arrayScVals); scVals.push(tupleScValsVec); continue; } // Vector case #2: array of primitives (homogeneous type) if (isPrimitiveArray(argValue)) { // TypeScript now knows argValue is SorobanArgumentValue[] thanks to type predicate const arrayScVals = argValue.reduce((acc: unknown[], v) => { const primitive = v as unknown as SorobanArgumentValue; if (primitive.type === 'bool') { acc.push(primitive.value === 'true' ? true : false); } else if (primitive.type === 'bytes') { const encoding = detectBytesEncoding(primitive.value as string); acc.push(stringToBytes(primitive.value as string, encoding)); } else { acc.push(primitive.value); } return acc; }, []); const firstItem = argValue[0]; const scValType = convertStellarTypeToScValType(firstItem.type); const typeHint = Array.isArray(scValType) ? scValType[0] : scValType; const scVal = nativeToScVal(arrayScVals, { type: typeHint, }); scVals.push(scVal); continue; } // Tuple case - mixed types in array if (isTupleArray(argValue)) { // TypeScript now knows argValue is SorobanArgumentValue[] thanks to type predicate const tupleScValsVec = convertTupleToScVal(argValue); scVals.push(tupleScValsVec); continue; } } // Object case - structured data if (isObjectWithTypedValues(argValue)) { const convertedObj = convertObjectToScVal(argValue as Record<string, SorobanArgumentValue>); scVals.push(convertedObj); continue; } // Single primitive value if (isPrimitiveValue(argValue)) { scVals.push(getScValFromPrimitive(argValue as SorobanArgumentValue)); } } return scVals; }