UNPKG

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

Version:
134 lines (123 loc) 4.42 kB
import { scValToNative, xdr } from '@stellar/stellar-sdk'; import type { ContractFunction } from '@openzeppelin/contracts-ui-builder-types'; import { bytesToHex, logger } from '@openzeppelin/contracts-ui-builder-utils'; import { isSerializableObject, stringifyWithBigInt } from '../utils'; /** * Formats the result of a Stellar view function call into a user-friendly string. * * @param result The result value (can be ScVal, native JS value, or other types). * @param functionDetails The contract function details. * @returns A string representation suitable for display. */ export function formatStellarFunctionResult( result: unknown, functionDetails: ContractFunction ): string { if (!functionDetails.outputs || !Array.isArray(functionDetails.outputs)) { logger.warn( 'formatStellarFunctionResult', `Output definition missing or invalid for function ${functionDetails.name}.` ); return '[Error: Output definition missing]'; } try { let valueToFormat: unknown; // Handle null/undefined values if (result === null || result === undefined) { return '(null)'; } // Check if result is an ScVal and convert to native JS value if (isScVal(result)) { try { // Special handling for void ScVal const scVal = result as xdr.ScVal; if (scVal.switch().name === 'scvVoid') { return '(void)'; } valueToFormat = scValToNative(scVal); // Convert Buffer to Uint8Array for cross-platform compatibility // scValToNative may return Buffer objects in some environments if ( valueToFormat && typeof valueToFormat === 'object' && 'constructor' in valueToFormat && valueToFormat.constructor?.name === 'Buffer' ) { valueToFormat = new Uint8Array(valueToFormat as ArrayLike<number>); } } catch (error) { logger.error('formatStellarFunctionResult', 'Failed to convert ScVal to native', { functionName: functionDetails.name, error, }); return '[Error: Failed to decode ScVal]'; } } else { valueToFormat = result; } // Format based on type if (typeof valueToFormat === 'bigint') { return valueToFormat.toString(); } else if (typeof valueToFormat === 'string') { return valueToFormat; } else if (typeof valueToFormat === 'number') { return valueToFormat.toString(); } else if (typeof valueToFormat === 'boolean') { return String(valueToFormat); } else if (valueToFormat instanceof Uint8Array) { // Handle byte arrays - convert to hex string return bytesToHex(valueToFormat, true); } else if (Array.isArray(valueToFormat)) { // Handle arrays/vectors if (valueToFormat.length === 0) { return '[]'; } // Use compact formatting for simple arrays, pretty formatting for complex ones if ( valueToFormat.every( (item) => typeof item === 'string' || typeof item === 'number' || typeof item === 'boolean' || typeof item === 'bigint' ) ) { return stringifyWithBigInt(valueToFormat); // No spacing for simple arrays } return stringifyWithBigInt(valueToFormat, 2); } else if (isSerializableObject(valueToFormat)) { // Handle objects/maps/structs if (Object.keys(valueToFormat as object).length === 0) { return '{}'; } return stringifyWithBigInt(valueToFormat, 2); } else if (valueToFormat === null || valueToFormat === undefined) { return '(null)'; } else { // Handle any other type by stringifying return stringifyWithBigInt(valueToFormat, 2); } } catch (error) { const errorMessage = `Error formatting result for ${functionDetails.name}: ${(error as Error).message}`; logger.error('formatStellarFunctionResult', errorMessage, { functionName: functionDetails.name, result, error, }); return `[${errorMessage}]`; } } /** * Type guard to check if a value is an ScVal */ function isScVal(value: unknown): boolean { if (!value || typeof value !== 'object') { return false; } try { // Use instanceof check for robust type detection return typeof xdr !== 'undefined' && xdr.ScVal && value instanceof xdr.ScVal; } catch { return false; } }