Skip to content

Commit ff5743e

Browse files
committed
feat: add ABI fetcher script for direct and proxy contracts via Filfox API
1 parent 6ddbac3 commit ff5743e

File tree

1 file changed

+94
-0
lines changed

1 file changed

+94
-0
lines changed
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import type { Address } from 'viem';
2+
3+
// Define the type for the contract object wagmi uses
4+
export type ContractConfig = {
5+
name: string;
6+
address: Address;
7+
chainId: number;
8+
isProxy?: boolean;
9+
};
10+
11+
interface FilfoxProxyResponse {
12+
proxyImpl: string | null;
13+
abi: string | null;
14+
}
15+
16+
// Unified ABI fetcher: routes to proxy or direct ABI fetch logic based on contract type.
17+
// If the contract is a proxy (isProxy=true), fetches the implementation ABI; otherwise, fetches the contract's own ABI.
18+
export async function fetchContractAbi(contract: ContractConfig): Promise<{ body: string }> {
19+
if (contract.isProxy) {
20+
return fetchProxyAbi(contract);
21+
} else {
22+
return fetchDirectAbi(contract);
23+
}
24+
}
25+
26+
/**
27+
* Fetches the ABI for contract's implementation from Filfox.
28+
* @param contract The contract configuration from wagmi.
29+
* @returns A promise resolving to the ABI content for wagmi.
30+
*/
31+
async function fetchDirectAbi(contract: ContractConfig): Promise<{ body: string }> {
32+
return fetchAbiFromAddress(contract.address, contract.name, contract.chainId);
33+
}
34+
35+
/**
36+
* Fetches the ABI for a proxy contract's implementation from Filfox.
37+
* @param contract The contract configuration from wagmi.
38+
* @returns A promise resolving to the ABI content for wagmi.
39+
*/
40+
async function fetchProxyAbi(contract: ContractConfig): Promise<{ body: string }> {
41+
// 1. Fetch the implementation address from the proxy contract.
42+
const proxyUrl = `https://filfox.info/api/v1/address/${contract.address}/contract`;
43+
const proxyResponse = await fetch(proxyUrl, {
44+
headers: { 'Accept': 'application/json' },
45+
});
46+
if (!proxyResponse.ok) {
47+
throw new Error(
48+
`Failed to fetch data for proxy contract. Status: ${proxyResponse.status}`,
49+
);
50+
}
51+
const proxyData = await proxyResponse.json() as FilfoxProxyResponse;
52+
const implementationAddress = proxyData?.proxyImpl;
53+
if (!implementationAddress || implementationAddress === "null") {
54+
throw new Error(
55+
`Could not find implementation address ('proxyImpl') in API response for ${contract.name}.`,
56+
);
57+
}
58+
59+
// 2. Fetch the ABI from the implementation contract using the helper
60+
return fetchAbiFromAddress(implementationAddress as Address, contract.name, contract.chainId);
61+
}
62+
63+
// Internal helper to fetch and parse ABI from Filfox for a given address
64+
async function fetchAbiFromAddress(address: Address, name: string, chainId: number): Promise<{ body: string }> {
65+
try {
66+
const url = `https://filfox.info/api/v1/address/${address}/contract`;
67+
const response = await fetch(url, {
68+
headers: { 'Accept': 'application/json' },
69+
});
70+
if (!response.ok) {
71+
throw new Error(
72+
`Failed to fetch data for contract. Status: ${response.status}`,
73+
);
74+
}
75+
const data = await response.json() as FilfoxProxyResponse;
76+
const abiString = data?.abi;
77+
if (typeof abiString !== 'string' || abiString.length === 0) {
78+
throw new Error(
79+
`Failed to parse ABI. The '.abi' field may be missing, null, or not a valid string.`
80+
);
81+
}
82+
const abi = JSON.parse(abiString);
83+
console.log(`Successfully fetched ABI for ${name}.`);
84+
return {
85+
body: JSON.stringify(abi, null, 2),
86+
};
87+
} catch (error) {
88+
console.error(
89+
`Failed to fetch ABI for ${name} on chain ${chainId}:`,
90+
error,
91+
);
92+
throw error;
93+
}
94+
}

0 commit comments

Comments
 (0)