diff --git a/services/database/migrations/20260816133800_add_compiled_contracts_metadata.sql b/services/database/migrations/20260816133800_add_compiled_contracts_metadata.sql new file mode 100644 index 0000000000..ca594a42c2 --- /dev/null +++ b/services/database/migrations/20260816133800_add_compiled_contracts_metadata.sql @@ -0,0 +1,22 @@ +-- migrate:up + +CREATE TABLE compiled_contracts_metadata ( + compilation_id uuid PRIMARY KEY REFERENCES compiled_contracts(id) ON DELETE CASCADE, + metadata json NOT NULL +); + +-- Backfill metadata from sourcify_matches. +-- We use DISTINCT ON (compilation_id) to ensure we only keep the "first" metadata variant per compilation, +-- which matches the logic used for source files. We also ensure we only insert metadata that is NOT NULL. +INSERT INTO compiled_contracts_metadata (compilation_id, metadata) +SELECT DISTINCT ON (vc.compilation_id) + vc.compilation_id, + sm.metadata +FROM sourcify_matches sm +JOIN verified_contracts vc ON vc.id = sm.verified_contract_id +WHERE sm.metadata IS NOT NULL +ON CONFLICT (compilation_id) DO NOTHING; + +-- migrate:down + +DROP TABLE IF EXISTS compiled_contracts_metadata; diff --git a/services/database/sourcify-database.sql b/services/database/sourcify-database.sql index fe8613234e..716dba44e1 100644 --- a/services/database/sourcify-database.sql +++ b/services/database/sourcify-database.sql @@ -986,6 +986,16 @@ CREATE TABLE public.compiled_contracts ( ); +-- +-- Name: compiled_contracts_metadata; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.compiled_contracts_metadata ( + compilation_id uuid NOT NULL, + metadata json NOT NULL +); + + -- -- Name: compiled_contracts_signatures; Type: TABLE; Schema: public; Owner: - -- @@ -1317,6 +1327,14 @@ ALTER TABLE ONLY public.code ADD CONSTRAINT code_pkey PRIMARY KEY (code_hash); +-- +-- Name: compiled_contracts_metadata compiled_contracts_metadata_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.compiled_contracts_metadata + ADD CONSTRAINT compiled_contracts_metadata_pkey PRIMARY KEY (compilation_id); + + -- -- Name: compiled_contracts compiled_contracts_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- @@ -2074,6 +2092,14 @@ ALTER TABLE ONLY public.compiled_contracts ADD CONSTRAINT compiled_contracts_creation_code_hash_fkey FOREIGN KEY (creation_code_hash) REFERENCES public.code(code_hash); +-- +-- Name: compiled_contracts_metadata compiled_contracts_metadata_compilation_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.compiled_contracts_metadata + ADD CONSTRAINT compiled_contracts_metadata_compilation_id_fkey FOREIGN KEY (compilation_id) REFERENCES public.compiled_contracts(id) ON DELETE CASCADE; + + -- -- Name: compiled_contracts compiled_contracts_runtime_code_hash_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- @@ -2210,4 +2236,5 @@ INSERT INTO public.schema_migrations (version) VALUES ('20260309080000'), ('20260527081526'), ('20260527085036'), - ('20260527085037'); + ('20260527085037'), + ('20260816133800'); diff --git a/services/server/src/server/services/storageServices/AbstractDatabaseService.ts b/services/server/src/server/services/storageServices/AbstractDatabaseService.ts index a072b36114..5f86904544 100644 --- a/services/server/src/server/services/storageServices/AbstractDatabaseService.ts +++ b/services/server/src/server/services/storageServices/AbstractDatabaseService.ts @@ -117,6 +117,11 @@ export default abstract class AbstractDatabaseService { sourcesInformation: databaseColumns.sourcesInformation, compilation_id: compiledContractId, }); + + await this.database.insertCompiledContractMetadata(client, { + compilation_id: compiledContractId, + metadata: databaseColumns.metadata, + }); } // insert new recompiled contract with newly added contract and compiledContract @@ -214,6 +219,11 @@ export default abstract class AbstractDatabaseService { sourcesInformation: databaseColumns.sourcesInformation, compilation_id: compiledContractId, }); + + await this.database.insertCompiledContractMetadata(client, { + compilation_id: compiledContractId, + metadata: databaseColumns.metadata, + }); } // update verified contract with the newly added recompiled contract diff --git a/services/server/src/server/services/utils/Database.ts b/services/server/src/server/services/utils/Database.ts index 0d6658d710..896fb1cbd4 100644 --- a/services/server/src/server/services/utils/Database.ts +++ b/services/server/src/server/services/utils/Database.ts @@ -165,7 +165,7 @@ export class Database { sourcify_matches.created_at, sourcify_matches.creation_match, sourcify_matches.runtime_match, - sourcify_matches.metadata, + COALESCE(compiled_contracts_metadata.metadata, sourcify_matches.metadata) as metadata, verified_contracts.creation_values, verified_contracts.runtime_values, verified_contracts.compilation_id, @@ -177,6 +177,7 @@ export class Database { FROM ${this.schema}.sourcify_matches JOIN ${this.schema}.verified_contracts ON verified_contracts.id = sourcify_matches.verified_contract_id JOIN ${this.schema}.compiled_contracts ON compiled_contracts.id = verified_contracts.compilation_id + LEFT JOIN ${this.schema}.compiled_contracts_metadata ON compiled_contracts_metadata.compilation_id = verified_contracts.compilation_id JOIN ${this.schema}.contract_deployments ON contract_deployments.id = verified_contracts.deployment_id AND contract_deployments.chain_id = $1 @@ -220,7 +221,12 @@ ${ onchain_runtime_code.code_hash, onchain_creation_code.code_hash, recompiled_runtime_code.code_hash, - recompiled_creation_code.code_hash` + recompiled_creation_code.code_hash${ + properties.includes("metadata") || + properties.includes("std_json_output") + ? ",\n compiled_contracts_metadata.compilation_id" + : "" + }` : ""; return await this.pool.query( @@ -239,6 +245,11 @@ ${ LEFT JOIN ${this.schema}.code as onchain_creation_code ON onchain_creation_code.code_hash = contracts.creation_code_hash LEFT JOIN ${this.schema}.code as recompiled_runtime_code ON recompiled_runtime_code.code_hash = compiled_contracts.runtime_code_hash LEFT JOIN ${this.schema}.code as recompiled_creation_code ON recompiled_creation_code.code_hash = compiled_contracts.creation_code_hash +${ + properties.includes("metadata") || properties.includes("std_json_output") + ? `LEFT JOIN ${this.schema}.compiled_contracts_metadata ON compiled_contracts_metadata.compilation_id = verified_contracts.compilation_id` + : "" +} ${ properties.includes("function_signatures") || properties.includes("event_signatures") || @@ -1239,4 +1250,18 @@ ${ ); } } + + async insertCompiledContractMetadata( + poolClient: PoolClient, + { compilation_id, metadata }: { compilation_id: string; metadata: any }, + ) { + if (metadata == null) return; + + await poolClient.query( + `INSERT INTO ${this.schema}.compiled_contracts_metadata (compilation_id, metadata) + VALUES ($1, $2) + ON CONFLICT (compilation_id) DO NOTHING`, + [compilation_id, metadata], + ); + } } diff --git a/services/server/src/server/services/utils/database-util.ts b/services/server/src/server/services/utils/database-util.ts index 23b01e61ca..f3e7466de1 100644 --- a/services/server/src/server/services/utils/database-util.ts +++ b/services/server/src/server/services/utils/database-util.ts @@ -236,6 +236,7 @@ export interface DatabaseColumns { "id" | "compilation_id" | "deployment_id" >; sourcesInformation: SourceInformation[]; + metadata: any; } export type GetVerifiedContractByChainAndAddressResult = @@ -435,7 +436,8 @@ export const STORED_PROPERTIES_TO_SELECTORS = { name: "compiled_contracts.name", fully_qualified_name: "compiled_contracts.fully_qualified_name", abi: "compiled_contracts.compilation_artifacts->'abi' as abi", - metadata: "sourcify_matches.metadata", + metadata: + "COALESCE(compiled_contracts_metadata.metadata, sourcify_matches.metadata) as metadata", storage_layout: "compiled_contracts.compilation_artifacts->'storageLayout' as storage_layout", transient_storage_layout: @@ -461,7 +463,7 @@ export const STORED_PROPERTIES_TO_SELECTORS = { json_build_object( split_part(compiled_contracts.fully_qualified_name, ':', -1), json_build_object( 'abi', compiled_contracts.compilation_artifacts->'abi', - 'metadata', cast(sourcify_matches.metadata as text), + 'metadata', cast(COALESCE(compiled_contracts_metadata.metadata, sourcify_matches.metadata) as text), 'userdoc', compiled_contracts.compilation_artifacts->'userdoc', 'devdoc', compiled_contracts.compilation_artifacts->'devdoc', 'storageLayout', compiled_contracts.compilation_artifacts->'storageLayout', @@ -922,6 +924,7 @@ export async function getDatabaseColumnsFromVerification( runtime_metadata_match, creation_metadata_match, }, + metadata: (verification.compilation as any).metadata, }; }