Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 89 additions & 0 deletions packages/synapse-sdk/src/script/abi-fetcher.ts
Original file line number Diff line number Diff line change
@@ -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
}
}
99 changes: 64 additions & 35 deletions packages/synapse-sdk/wagmi.config.ts
Original file line number Diff line number Diff line change
@@ -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<Config[]> => {
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