diff --git a/README.md b/README.md index e20b6fe..f018d09 100644 --- a/README.md +++ b/README.md @@ -26,12 +26,25 @@ What can WhatsABI do? ## Usage +Quick start: + ```typescript import { ethers } from "ethers"; import { whatsabi } from "@shazow/whatsabi"; const provider = ethers.getDefaultProvider(); // substitute with your fav provider const address = "0x00000000006c3852cbEf3e08E8dF289169EdE581"; // Or your fav contract address + +// Quick-start: + +const result = await whatsabi.autoload(address, { provider }); +console.log(result.abi); +// -> [ ... ] +``` + +Breaking it down: + +```typescript const code = await provider.getCode(address); // Load the bytecode // Get just the callable selectors @@ -62,16 +75,16 @@ console.log(await signatureLookup.loadEvents("0x721c20121297512b72821b97f5326877 // Here's a multiloader with an Etherscan API key, it can be used with autoload below. // Each source will be attempted until a result is found. -const abiLoader = new whatsabi.loaders.MultiABILoader([ +const loader = new whatsabi.loaders.MultiABILoader([ new whatsabi.loaders.SourcifyABILoader(), new whatsabi.loaders.EtherscanABILoader({ apiKey: "...", // Replace the value with your Etherscan API key }), ]); -console.log(await abiLoader.loadABI(address)); +const { abi, name, /* ... other metadata */ } = await loader.getContract(address)); ``` -Bonus do-all-the-things helper: +All together with our do-all-the-things helper: ```typescript ... diff --git a/src/loaders.ts b/src/loaders.ts index 7492f7b..be1ad0e 100644 --- a/src/loaders.ts +++ b/src/loaders.ts @@ -1,16 +1,28 @@ import { addressWithChecksum, fetchJSON } from "./utils.js"; -export interface ABILoader { - loadABI(address: string): Promise; - getContract(address: string): Promise -} - -export interface ContractData { +export type ContractResult = { abi: any[]; name: string | null; evmVersion: string; compilerVersion: string; runs: number; + + ok: boolean; // False if no result is found +} + +const emptyContractResult : ContractResult = { + ok: false, + + abi: [], + name: null, + evmVersion: "", + compilerVersion: "", + runs: 0, +} + +export interface ABILoader { + loadABI(address: string): Promise; + getContract(address: string): Promise } // Load ABIs from multiple providers until a result is found. @@ -21,7 +33,7 @@ export class MultiABILoader implements ABILoader { this.loaders = loaders; } - async getContract(address: string): Promise { + async getContract(address: string): Promise { for (const loader of this.loaders) { try { const r = await loader.getContract(address); @@ -31,7 +43,7 @@ export class MultiABILoader implements ABILoader { throw error; } } - return null + return emptyContractResult; } async loadABI(address: string): Promise { @@ -55,13 +67,13 @@ export class EtherscanABILoader implements ABILoader { this.baseURL = config.baseURL || "https://api.etherscan.io/api"; } - async getContract(address: string): Promise { + async getContract(address: string): Promise { let url = this.baseURL + '?module=contract&action=getsourcecode&address=' + address; if (this.apiKey) url += "&apikey=" + this.apiKey; const r = await fetchJSON(url); if (r.status === "0") { - if (r.result === "Contract source code not verified") return null; + if (r.result === "Contract source code not verified") return emptyContractResult; throw new Error("Etherscan error: " + r.result); } @@ -73,6 +85,8 @@ export class EtherscanABILoader implements ABILoader { evmVersion: result.EVMVersion, compilerVersion: result.CompilerVersion, runs: result.Runs, + + ok: true, }; } @@ -107,7 +121,7 @@ export class SourcifyABILoader implements ABILoader { this.chainId = config?.chainId ?? 1; } - async getContract(address: string): Promise { + async getContract(address: string): Promise { // Sourcify doesn't like it when the address is not checksummed address = addressWithChecksum(address); @@ -120,6 +134,8 @@ export class SourcifyABILoader implements ABILoader { evmVersion: r.settings.evmVersion, compilerVersion: r.compiler.version, runs: r.settings.optimizer.runs, + + ok: true, }; } catch (error: any) { if (!isSourcifyNotFound(error)) throw error; @@ -134,12 +150,14 @@ export class SourcifyABILoader implements ABILoader { evmVersion: r.settings.evmVersion, compilerVersion: r.compiler.version, runs: r.settings.optimizer.runs, + + ok: true, }; } catch (error: any) { if (!isSourcifyNotFound(error)) throw error; } - return null + return emptyContractResult; } async loadABI(address: string): Promise {