Skip to content
Merged
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
21 changes: 11 additions & 10 deletions src/app/service-providers/data/column-definition.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { ID } from '@filecoin-foundation/ui-filecoin/Table/ID'
import { YesNoStatus } from '@filecoin-foundation/ui-filecoin/Table/YesNoStatus'
// import { ExternalTextLink } from '@filecoin-foundation/ui-filecoin/TextLink/ExternalTextLink'
import { createColumnHelper } from '@tanstack/react-table'

import { CompactPeerID } from '@/components/CompactPeerID'
import { PdpScanLink } from '@/components/PdpScanLink'
import { ProviderOverview } from '@/components/ProviderOverview'
import { SoftwareVersion } from '@/components/SoftwareVersion'

Expand Down Expand Up @@ -52,15 +52,16 @@ export const columns = [
sortingFn: sortSoftwareVersion,
sortUndefined: 'last',
}),
// TODO: Add check activity link
// columnHelper.accessor('checkActivityUrl', {
// header: 'Check Activity',
// cell: (info) => (
// <ExternalTextLink href={info.getValue() || '#todo'}>
// View on PDP Scan
// </ExternalTextLink>
// ),
// }),
columnHelper.accessor('checkActivityUrl', {
header: 'Check Activity',
cell: (info) => (
<PdpScanLink
pdpScanUrl={info.getValue()}
providerName={info.row.original.name}
/>
),
enableSorting: false,
}),
// TODO: Add Service Offered Column
columnHelper.accessor('serviceStatus', {
header: 'Status',
Expand Down
21 changes: 11 additions & 10 deletions src/app/warm-storage-service/data/column-definition.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { ID } from '@filecoin-foundation/ui-filecoin/Table/ID'
// import { ExternalTextLink } from '@filecoin-foundation/ui-filecoin/TextLink/ExternalTextLink'
import { createColumnHelper } from '@tanstack/react-table'

import { CompactPeerID } from '@/components/CompactPeerID'
import { PdpScanLink } from '@/components/PdpScanLink'
import { ProviderOverview } from '@/components/ProviderOverview'
import { SoftwareVersion } from '@/components/SoftwareVersion'

Expand Down Expand Up @@ -45,15 +45,16 @@ export const columns = [
sortingFn: sortSoftwareVersion,
sortUndefined: 'last',
}),
// TODO: Add check activity link
// columnHelper.accessor('checkActivityUrl', {
// header: 'Check Activity',
// cell: (info) => (
// <ExternalTextLink href={info.getValue() || '#todo'}>
// View on PDP Scan
// </ExternalTextLink>
// ),
// }),
columnHelper.accessor('checkActivityUrl', {
header: 'Check Activity',
cell: (info) => (
<PdpScanLink
pdpScanUrl={info.getValue()}
providerName={info.row.original.name}
/>
),
enableSorting: false,
}),
columnHelper.accessor('location', {
header: 'Location',
cell: (info) => info.getValue(),
Expand Down
17 changes: 17 additions & 0 deletions src/components/PdpScanLink.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { ExternalTextLink } from '@filecoin-foundation/ui-filecoin/TextLink/ExternalTextLink'

type PdpScanLinkProps = {
pdpScanUrl: string
providerName: string
}

export function PdpScanLink({ pdpScanUrl, providerName }: PdpScanLinkProps) {
return (
<ExternalTextLink
href={pdpScanUrl}
aria-label={`View activity for provider ${providerName} on PDP Scan`}
>
View on PDP Scan
</ExternalTextLink>
)
}
2 changes: 1 addition & 1 deletion src/schemas/provider-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const ethereumAddressSchema = z.custom<Address>(
export const providerSchema = z.object({
capabilityKeys: z.array(z.string()),
capacityTb: z.bigint().optional(),
// checkActivityUrl: z.url().optional(), // TODO: Remove optional
checkActivityUrl: z.url(),
description: z.string(),
id: z.number(),
ipniIpfs: z.boolean(),
Expand Down
8 changes: 4 additions & 4 deletions src/services/providers/contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type { ServiceRegistryABI, WarmStorageViewABI } from '@/config/abis'

import { BATCH_SIZE, PDP_PRODUCT_TYPE, PROVIDER_FETCH_LIMIT } from './constants'
import { processProviderData } from './processor'
import type { ProviderWithoutSoftwareVersion } from './types'
import type { BaseProviderData } from './types'

/**
* Fetch approved provider IDs from WarmStorageView contract with pagination
Expand Down Expand Up @@ -50,8 +50,8 @@ export async function fetchProvidersBulk(
>,
onlyActive: boolean,
approvedProviderIds?: Set<bigint>,
): Promise<ProviderWithoutSoftwareVersion[]> {
const providers: ProviderWithoutSoftwareVersion[] = []
): Promise<BaseProviderData[]> {
const providers: BaseProviderData[] = []
let offset = 0
let hasMore = true

Expand Down Expand Up @@ -117,7 +117,7 @@ export async function fetchProviderById(
PublicClient
>,
isApproved = false,
): Promise<ProviderWithoutSoftwareVersion | undefined> {
): Promise<BaseProviderData | undefined> {
const providerData =
await serviceRegistryContract.read.getProviderWithProduct([
providerId,
Expand Down
29 changes: 16 additions & 13 deletions src/services/providers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@ import {
type ServiceProvider,
} from '@/schemas/provider-schema'
import type { FetchProvidersOptions, ProviderFilter } from '@/types/providers'
import { getCheckActivityUrl } from '@/utils/provider-urls'

import { VERSION_FETCH_CONCURRENCY } from './constants'
import {
fetchApprovedProviderIds,
fetchProviderById,
fetchProvidersBulk,
} from './contract'
import type { ProviderWithoutSoftwareVersion } from './types'
import type { BaseProviderData } from './types'
import { fetchSoftwareVersion } from './version'

/**
Expand All @@ -34,7 +35,7 @@ async function fetchProvidersByFilter(
PublicClient
>
},
): Promise<ProviderWithoutSoftwareVersion[]> {
): Promise<BaseProviderData[]> {
const { storageView, serviceRegistry } = contracts

// Fetch approved provider IDs for marking providers
Expand Down Expand Up @@ -70,31 +71,33 @@ async function fetchProvidersByFilter(

const providers = await Promise.all(providerPromises)
return providers.filter(
(provider): provider is ProviderWithoutSoftwareVersion =>
provider !== null,
(provider): provider is BaseProviderData => provider !== null,
)
}

throw new Error(`Invalid filter type: ${filter}`)
}

/**
* Enrich providers with software version information
* Enrich providers with additional information (software version and check activity URL)
*/
async function enrichProvidersWithVersions(
providers: ProviderWithoutSoftwareVersion[],
async function enrichProviders(
providers: BaseProviderData[],
network: Network,
): Promise<ServiceProvider[]> {
const providersWithVersions: Array<
ProviderWithoutSoftwareVersion & { softwareVersion?: string }
> = []
const providersWithVersions: ServiceProvider[] = []

// Process providers in batches
for (let i = 0; i < providers.length; i += VERSION_FETCH_CONCURRENCY) {
const batch = providers.slice(i, i + VERSION_FETCH_CONCURRENCY)
const batchResults = await Promise.all(
batch.map(async (provider) => {
const softwareVersion = await fetchSoftwareVersion(provider.serviceUrl)
return { ...provider, softwareVersion }
const checkActivityUrl = getCheckActivityUrl(
network,
provider.payeeAddress,
)
Comment on lines +96 to +99
Copy link

Copilot AI Jan 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The checkActivityUrl is computed for every provider in a batch, but this is a synchronous operation that doesn't need to be inside the async map. Consider moving it outside the Promise.all to improve clarity and potentially performance, or compute it in a separate pass before the async operations.

Copilot uses AI. Check for mistakes.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that this is synchronous operation but separation would require two iterations without meaningful benefit

return { ...provider, softwareVersion, checkActivityUrl }
}),
)
providersWithVersions.push(...batchResults)
Expand Down Expand Up @@ -178,6 +181,6 @@ export async function fetchProviders(
serviceRegistry,
})

// Enrich with software versions
return enrichProvidersWithVersions(fetchedProviders)
// Enrich with additional information
return enrichProviders(fetchedProviders, network)
}
4 changes: 2 additions & 2 deletions src/services/providers/processor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type { ProviderData } from '@/types/contract-types'
import { decodePDPCapabilities } from '@/utils/decode-pdp-capabilities'
import { parseLocation } from '@/utils/parse-location'

import type { ProviderWithoutSoftwareVersion } from './types'
import type { BaseProviderData } from './types'

const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'

Expand Down Expand Up @@ -40,7 +40,7 @@ function buildCapabilities(
export function processProviderData(
providerData: ProviderData,
isApproved = false,
): ProviderWithoutSoftwareVersion | undefined {
): BaseProviderData | undefined {
const { providerId, providerInfo, product, productCapabilityValues } =
providerData

Expand Down
6 changes: 3 additions & 3 deletions src/services/providers/types.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import type { ServiceProvider } from '@/schemas/provider-schema'

/**
* Provider without software version (before version fetch)
* Base provider data from contract (before enrichment with software version and check activity URL)
*/
export type ProviderWithoutSoftwareVersion = Omit<
export type BaseProviderData = Omit<
ServiceProvider,
'softwareVersion'
'softwareVersion' | 'checkActivityUrl'
>
18 changes: 18 additions & 0 deletions src/utils/provider-urls.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import type { Address } from 'viem'

import type { Network } from '@/config/chains'
import { EXPLORERS } from '@/constants/external-services'

/**
* Constructs the check activity URL for a provider on PDP Scan
*
* @param network - The network (mainnet or calibration)
* @param payeeAddress - The provider's payee address
* @returns The full URL to view provider activity on PDP Scan
*/
export function getCheckActivityUrl(
network: Network,
payeeAddress: Address,
): string {
return `${EXPLORERS.PDP_SCAN[network]}${payeeAddress}`
}
Loading