From 1f5167aded7b45cb7d937c210da3a4400aba774b Mon Sep 17 00:00:00 2001 From: defiQUG Date: Wed, 29 Apr 2026 06:21:36 -0700 Subject: [PATCH] Expose full verified contract source payloads --- frontend/src/services/api/contracts.ts | 54 ++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/frontend/src/services/api/contracts.ts b/frontend/src/services/api/contracts.ts index 085d2cd..0b8e7d4 100644 --- a/frontend/src/services/api/contracts.ts +++ b/frontend/src/services/api/contracts.ts @@ -18,6 +18,11 @@ export interface ContractMethodExecutionResult { value: string } +export interface ContractSourceFile { + path: string + content: string +} + export interface ContractProfile { has_custom_methods_read: boolean has_custom_methods_write: boolean @@ -36,7 +41,10 @@ export interface ContractProfile { license_type?: string constructor_arguments?: string abi?: string + abi_full?: string + source_code_full?: string source_code_preview?: string + source_files: ContractSourceFile[] source_status_text?: string read_methods: ContractMethod[] write_methods: ContractMethod[] @@ -63,6 +71,7 @@ interface ContractCompatibilityAbiResponse { interface ContractCompatibilitySourceRecord { Address?: string ContractName?: string + FileName?: string CompilerVersion?: string OptimizationUsed?: string | number Runs?: string | number @@ -111,6 +120,47 @@ function normalizeNumber(value: string | number | null | undefined): number | un return undefined } +function displaySourcePath(record: ContractCompatibilitySourceRecord | undefined): string { + const fileName = record?.FileName?.trim() + if (fileName) return fileName + const contractName = record?.ContractName?.trim() + if (contractName) return contractName.endsWith('.sol') ? contractName : `${contractName}.sol` + return 'Contract.sol' +} + +function parseSourceFiles(sourceCode: string | undefined, record?: ContractCompatibilitySourceRecord): ContractSourceFile[] { + const trimmed = sourceCode?.trim() + if (!trimmed) return [] + + const candidates = [trimmed] + if (trimmed.startsWith('{{') && trimmed.endsWith('}}')) { + candidates.push(trimmed.slice(1, -1)) + } + + for (const candidate of candidates) { + try { + const parsed = JSON.parse(candidate) as { + sources?: Record + } + if (parsed && typeof parsed === 'object' && parsed.sources && typeof parsed.sources === 'object') { + return Object.entries(parsed.sources) + .map(([path, value]) => ({ + path, + content: typeof value === 'string' ? value : value?.content || '', + })) + .filter((file) => file.content.trim().length > 0) + } + } catch {} + } + + return [ + { + path: displaySourcePath(record), + content: trimmed, + }, + ] +} + function parseABI(abiString?: string): ContractMethod[] { if (!abiString) return [] try { @@ -359,6 +409,7 @@ export const contractsApi = { ? sourceRecord.ABI : undefined const sourceCode = sourceRecord?.SourceCode + const sourceFiles = parseSourceFiles(sourceCode, sourceRecord) const parsedMethods = parseABI(abiString) const sourceVerified = Boolean( abiString || @@ -391,7 +442,10 @@ export const contractsApi = { license_type: sourceRecord?.LicenseType || undefined, constructor_arguments: truncateHex(sourceRecord?.ConstructorArguments, 90), abi: truncateText(abiString, 1200), + abi_full: abiString, + source_code_full: sourceCode, source_code_preview: truncateText(sourceCode, 1200), + source_files: sourceFiles, source_status_text: sourceStatusText || undefined, read_methods: parsedMethods.filter(isReadMethod), write_methods: parsedMethods.filter((method) => !isReadMethod(method)),