diff --git a/packages/synapse-sdk/src/script/abi-fetcher.ts b/packages/synapse-sdk/src/script/abi-fetcher.ts new file mode 100644 index 00000000..3f764394 --- /dev/null +++ b/packages/synapse-sdk/src/script/abi-fetcher.ts @@ -0,0 +1,89 @@ +import type { Address } from 'viem' + +// Define the type for the contract object wagmi uses +export type ContractConfig = { + name: string + address: Address + chainId: number + isProxy?: boolean +} + +interface FilfoxProxyResponse { + proxyImpl: string | null + abi: string | null +} + +// Unified ABI fetcher: routes to proxy or direct ABI fetch logic based on contract type. +// If the contract is a proxy (isProxy=true), fetches the implementation ABI; otherwise, fetches the contract's own ABI. +export async function fetchContractAbi(contract: ContractConfig): Promise<{ body: string }> { + if (contract.isProxy) { + return fetchProxyAbi(contract) + } else { + return fetchDirectAbi(contract) + } +} + +/** + * Fetches the ABI for contract's implementation from Filfox. + * @param contract The contract configuration from wagmi. + * @returns A promise resolving to the ABI content for wagmi. + */ +async function fetchDirectAbi(contract: ContractConfig): Promise<{ body: string }> { + return fetchAbiFromAddress(contract.address, contract.name, contract.chainId) +} + +/** + * Fetches the ABI for a proxy contract's implementation from Filfox. + * @param contract The contract configuration from wagmi. + * @returns A promise resolving to the ABI content for wagmi. + */ +async function fetchProxyAbi(contract: ContractConfig): Promise<{ body: string }> { + // 1. Fetch the implementation address from the proxy contract. + const proxyUrl = + contract.chainId === 314 + ? `https://filfox.info/api/v1/address/${contract.address}/contract` + : `https://calibration.filfox.info/api/v1/address/${contract.address}/contract` + const proxyResponse = await fetch(proxyUrl, { + headers: { Accept: 'application/json' }, + }) + if (!proxyResponse.ok) { + throw new Error(`Failed to fetch data for proxy contract. Status: ${proxyResponse.status}`) + } + const proxyData = (await proxyResponse.json()) as FilfoxProxyResponse + const implementationAddress = proxyData?.proxyImpl + if (!implementationAddress || implementationAddress === 'null') { + throw new Error(`Could not find implementation address ('proxyImpl') in API response for ${contract.name}.`) + } + + // 2. Fetch the ABI from the implementation contract using the helper + return fetchAbiFromAddress(implementationAddress as Address, contract.name, contract.chainId) +} + +// Internal helper to fetch and parse ABI from Filfox for a given address +async function fetchAbiFromAddress(address: Address, name: string, chainId: number): Promise<{ body: string }> { + try { + const url = + chainId === 314 + ? `https://filfox.info/api/v1/address/${address}/contract` + : `https://calibration.filfox.info/api/v1/address/${address}/contract` + const response = await fetch(url, { + headers: { Accept: 'application/json' }, + }) + if (!response.ok) { + throw new Error(`Failed to fetch data for contract. Status: ${response.status}`) + } + const data = (await response.json()) as FilfoxProxyResponse + const abiString = data?.abi + if (typeof abiString !== 'string' || abiString.length === 0) { + throw new Error(`Failed to parse ABI. The '.abi' field may be missing, null, or not a valid string.`) + } + const abi = JSON.parse(abiString) + console.log(`Successfully fetched ABI for ${name}.`) + return { + body: JSON.stringify(abi, null, 2), + } + } catch (error) { + console.error(`Failed to fetch ABI for ${name} on chain ${chainId}:`, error) + throw error + } +} diff --git a/packages/synapse-sdk/wagmi.config.ts b/packages/synapse-sdk/wagmi.config.ts index a7eed4ce..b0c95591 100644 --- a/packages/synapse-sdk/wagmi.config.ts +++ b/packages/synapse-sdk/wagmi.config.ts @@ -1,72 +1,101 @@ -import { defineConfig } from '@wagmi/cli' -import { fetch } from '@wagmi/cli/plugins' -import type { Address } from 'viem' +import type { Config } from '@wagmi/cli' +import type { Abi, Address } from 'viem' +import { fetchContractAbi } from './src/script/abi-fetcher.ts' -const config = defineConfig(() => { +const network = process.env.NETWORK === 'mainnet' ? 'mainnet' : 'calibration' +const chainId = network === 'mainnet' ? 314 : 314159 + +const createConfig = async (): Promise => { const contracts = [ { name: 'Payments', + isProxy: false, address: { - 314: '0x0000000000000000000000000000000000000000' as Address, - 314159: '0x0000000000000000000000000000000000000000' as Address, + 314: '0x7DaE6F488651ec5CEE38c9DFbd7d31223eAe1DDE' as Address, + 314159: '0x6dB198201F900c17e86D267d7Df82567FB03df5E' as Address, }, }, { name: 'FilecoinWarmStorageService', + isProxy: true, address: { 314: '0x0000000000000000000000000000000000000000' as Address, - 314159: '0x0000000000000000000000000000000000000000' as Address, + 314159: '0x468342072e0dc86AFFBe15519bc5B1A1aa86e4dc' as Address, }, }, { name: 'FilecoinWarmStorageServiceStateView', + isProxy: false, address: { 314: '0x0000000000000000000000000000000000000000' as Address, - 314159: '0x0000000000000000000000000000000000000000' as Address, + 314159: '0xE4587AAdB97d7B8197aa08E432bAD0D9Cfe3a17F' as Address, }, }, { name: 'PDPVerifier', + isProxy: true, address: { 314: '0x0000000000000000000000000000000000000000' as Address, - 314159: '0x0000000000000000000000000000000000000000' as Address, + 314159: '0x579dD9E561D4Cd1776CF3e52E598616E77D5FBcb' as Address, }, }, + // { + // name: 'ServiceProviderRegistry', + // isProxy: true, + // address: { + // 314: '0x0000000000000000000000000000000000000000' as Address, + // 314159: '0x1096ba1e7BB912136DA8524A22bF71091dc4FDd9' as Address, + // }, + // }, + // { + // name: 'SessionKeyRegistry', + // isProxy: false, + // address: { + // 314: '0x0000000000000000000000000000000000000000' as Address, + // 314159: '0x97Dd879F5a97A8c761B94746d7F5cfF50AAd4452' as Address, + // }, + // }, { - name: 'ServiceProviderRegistry', + name: 'Usdfc', + isProxy: true, address: { - 314: '0x0000000000000000000000000000000000000000' as Address, - 314159: '0x0000000000000000000000000000000000000000' as Address, - }, - }, - { - name: 'SessionKeyRegistry', - address: { - 314: '0x0000000000000000000000000000000000000000' as Address, - 314159: '0x0000000000000000000000000000000000000000' as Address, + 314: '0x80B98d3aa09ffff255c3ba4A241111Ff1262F045' as Address, + 314159: '0xb3042734b608a1B16e9e86B374A3f3e389B4cDf0' as Address, }, }, ] + // Filter addresses for the selected network + const filteredContracts = contracts.map((contract) => ({ + ...contract, + address: typeof contract.address === 'object' ? contract.address[chainId] : contract.address, + chainId, + })) + + // Fetch all ABIs upfront + const contractsWithAbis = await Promise.all( + filteredContracts.map(async (contract) => { + const result = await fetchContractAbi({ + name: contract.name, + address: contract.address as Address, + chainId: contract.chainId, + isProxy: contract.isProxy, + }) + return { + name: contract.name, + address: contract.address as Address, + abi: JSON.parse(result.body) as Abi, + } + }) + ) + return [ { out: 'src/abis/gen.ts', - plugins: [ - fetch({ - contracts, - cacheDuration: 100, - request(contract) { - const baseUrl = - 'https://raw.githubusercontent.com/FilOzone/filecoin-services/refs/heads/main/service_contracts/abi' - - return { - url: `${baseUrl}/${contract.name}.abi.json`, - } - }, - }), - ], + contracts: contractsWithAbis, + plugins: [], }, ] -}) +} -export default config +export default createConfig