diff --git a/etc/sdk.api.md b/etc/sdk.api.md index 44742e472..c5748e623 100644 --- a/etc/sdk.api.md +++ b/etc/sdk.api.md @@ -1873,6 +1873,12 @@ export class FetchError extends Error { // @internal (undocumented) export function fetchPreDeployMetadata(publishMetadataUri: string, storage: IStorage): Promise; +// Warning: (ae-forgotten-export) The symbol "ContractSource" needs to be exported by the entry point index.d.ts +// Warning: (ae-internal-missing-underscore) The name "fetchSourceFilesFromMetadata" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal (undocumented) +export function fetchSourceFilesFromMetadata(publishedMetadata: PublishedMetadata, storage: IStorage): Promise; + // Warning: (ae-internal-missing-underscore) The name "FileNameMissingError" should be prefixed with an underscore because the declaration is marked as @internal // // @internal (undocumented) diff --git a/src/common/feature-detection.ts b/src/common/feature-detection.ts index d32f36c96..061177f47 100644 --- a/src/common/feature-detection.ts +++ b/src/common/feature-detection.ts @@ -5,6 +5,7 @@ import { AbiFunction, AbiSchema, AbiTypeSchema, + ContractSource, PreDeployMetadata, PreDeployMetadataFetched, PublishedMetadata, @@ -262,14 +263,46 @@ async function fetchContractMetadata( const metadata = await storage.get(compilerMetadataUri); const abi = AbiSchema.parse(metadata.output.abi); const compilationTarget = metadata.settings.compilationTarget; - const keys = Object.keys(compilationTarget); - const name = compilationTarget[keys[0]]; + const targets = Object.keys(compilationTarget); + const name = compilationTarget[targets[0]]; return { name, abi, + metadata, }; } +/** + * @internal + * @param publishedMetadata + * @param storage + */ +export async function fetchSourceFilesFromMetadata( + publishedMetadata: PublishedMetadata, + storage: IStorage, +): Promise { + return await Promise.all( + Object.entries(publishedMetadata.metadata.sources).map( + async ([path, info]) => { + const urls = (info as any).urls as string[]; + const ipfsLink = urls.find((url) => url.includes("ipfs")); + if (ipfsLink) { + const ipfsHash = ipfsLink.split("ipfs/")[1]; + return { + filename: path, + source: await storage.getRaw(`ipfs://${ipfsHash}`), + }; + } else { + return { + filename: path, + source: "Could not find source for this contract", + }; + } + }, + ), + ); +} + /** * @internal * @param publishMetadataUri diff --git a/src/core/classes/contract-publisher.ts b/src/core/classes/contract-publisher.ts index 77282c231..08e4e4246 100644 --- a/src/core/classes/contract-publisher.ts +++ b/src/core/classes/contract-publisher.ts @@ -17,10 +17,12 @@ import { extractFunctions, fetchContractMetadataFromAddress, fetchPreDeployMetadata, + fetchSourceFilesFromMetadata, } from "../../common/feature-detection"; import { AbiFunction, ContractParam, + ContractSource, PublishedContract, PublishedContractSchema, } from "../../schema/contracts/custom"; @@ -99,6 +101,17 @@ export class ContractPublisher extends RPCConnectionHandler { ); } + /** + * @internal + * @param address + */ + public async fetchContractSourcesFromAddress( + address: string, + ): Promise { + const metadata = await this.fetchContractMetadataFromAddress(address); + return await fetchSourceFilesFromMetadata(metadata, this.storage); + } + /** * @interface * @param publisherAddress diff --git a/src/core/classes/contract-wrapper.ts b/src/core/classes/contract-wrapper.ts index a601b5fc2..cf2aca114 100644 --- a/src/core/classes/contract-wrapper.ts +++ b/src/core/classes/contract-wrapper.ts @@ -320,7 +320,7 @@ export class ContractWrapper< fn ]; if (!func) { - throw new Error("invalid function"); + throw new Error(`invalid function: "${fn.toString()}"`); } try { return await func(...args, callOverrides); diff --git a/src/schema/contracts/custom.ts b/src/schema/contracts/custom.ts index bb93936fc..b47eff1ab 100644 --- a/src/schema/contracts/custom.ts +++ b/src/schema/contracts/custom.ts @@ -119,7 +119,12 @@ export type AbiFunction = { signature: string; stateMutability: string; }; +export type ContractSource = { + filename: string; + source: string; +}; export type PublishedMetadata = { name: string; abi: z.infer; + metadata: Record; };