Skip to content

Commit

Permalink
Merge pull request #81 from shazow/loaders-patch
Browse files Browse the repository at this point in the history
loaders: getContract returns non-nullable ContractResult
  • Loading branch information
shazow committed Jan 22, 2024
2 parents 0e020a2 + 1bed4d0 commit 8166252
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 15 deletions.
19 changes: 16 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
...
Expand Down
42 changes: 30 additions & 12 deletions src/loaders.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,28 @@
import { addressWithChecksum, fetchJSON } from "./utils.js";

export interface ABILoader {
loadABI(address: string): Promise<any[]>;
getContract(address: string): Promise<ContractData | null>
}

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<any[]>;
getContract(address: string): Promise<ContractResult>
}

// Load ABIs from multiple providers until a result is found.
Expand All @@ -21,7 +33,7 @@ export class MultiABILoader implements ABILoader {
this.loaders = loaders;
}

async getContract(address: string): Promise<ContractData | null> {
async getContract(address: string): Promise<ContractResult> {
for (const loader of this.loaders) {
try {
const r = await loader.getContract(address);
Expand All @@ -31,7 +43,7 @@ export class MultiABILoader implements ABILoader {
throw error;
}
}
return null
return emptyContractResult;
}

async loadABI(address: string): Promise<any[]> {
Expand All @@ -55,13 +67,13 @@ export class EtherscanABILoader implements ABILoader {
this.baseURL = config.baseURL || "https://api.etherscan.io/api";
}

async getContract(address: string): Promise<ContractData | null> {
async getContract(address: string): Promise<ContractResult> {
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);
}
Expand All @@ -73,6 +85,8 @@ export class EtherscanABILoader implements ABILoader {
evmVersion: result.EVMVersion,
compilerVersion: result.CompilerVersion,
runs: result.Runs,

ok: true,
};
}

Expand Down Expand Up @@ -107,7 +121,7 @@ export class SourcifyABILoader implements ABILoader {
this.chainId = config?.chainId ?? 1;
}

async getContract(address: string): Promise<ContractData | null> {
async getContract(address: string): Promise<ContractResult> {
// Sourcify doesn't like it when the address is not checksummed
address = addressWithChecksum(address);

Expand All @@ -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;
Expand All @@ -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<any[]> {
Expand Down

0 comments on commit 8166252

Please sign in to comment.