filecoin-pin
Version:
IPFS Pinning Service API implementation that pins to Filecoin's PDP service
204 lines (181 loc) • 5.6 kB
text/typescript
import { Synapse, StorageService, RPC_URLS, type SynapseOptions, type UploadCallbacks } from '@filoz/synapse-sdk'
import type { Logger } from 'pino'
import type { Config } from './config.js'
let synapseInstance: Synapse | null = null
let storageInstance: StorageService | null = null
/**
* Reset the service instances (for testing)
*/
export function resetSynapseService (): void {
synapseInstance = null
storageInstance = null
}
export interface SynapseService {
synapse: Synapse
storage: StorageService
}
/**
* Initialize Synapse SDK and create storage service
*/
export async function initializeSynapse (config: Config, logger: Logger): Promise<SynapseService> {
// Log the configuration status
logger.info({
hasPrivateKey: config.privateKey != null,
rpcUrl: config.rpcUrl
}, 'Initializing Synapse')
// Check if Synapse is configured
if (config.privateKey == null) {
throw new Error('PRIVATE_KEY environment variable is required')
}
try {
logger.info({ event: 'synapse.init' }, 'Initializing Synapse SDK')
// Create Synapse instance
const synapseOptions: SynapseOptions = {
privateKey: config.privateKey,
rpcURL: config.rpcUrl ?? RPC_URLS.calibration.websocket
}
if (config.pandoraAddress != null) {
synapseOptions.pandoraAddress = config.pandoraAddress
}
const synapse = await Synapse.create(synapseOptions)
// Get network info for logging
const network = synapse.getNetwork()
logger.info({
event: 'synapse.init',
network,
rpcUrl: synapseOptions.rpcURL
}, 'Synapse SDK initialized')
// Create storage service
logger.info({ event: 'synapse.storage.create' }, 'Creating storage service')
const storage = await synapse.createStorage({
withCDN: false,
callbacks: {
onProviderSelected: (provider) => {
logger.info({
event: 'synapse.storage.provider_selected',
provider: {
owner: provider.owner,
pdpUrl: provider.pdpUrl,
pieceRetrievalUrl: provider.pieceRetrievalUrl
}
}, 'Selected storage provider')
},
onProofSetResolved: (info) => {
logger.info({
event: 'synapse.storage.proof_set_resolved',
proofSetId: info.proofSetId,
isExisting: info.isExisting,
provider: info.provider.owner
}, info.isExisting ? 'Using existing proof set' : 'Created new proof set')
},
onProofSetCreationStarted: (transaction, statusUrl) => {
logger.info({
event: 'synapse.storage.proof_set_creation_started',
txHash: transaction.hash,
statusUrl
}, 'Proof set creation transaction submitted')
},
onProofSetCreationProgress: (status) => {
logger.info({
event: 'synapse.storage.proof_set_creation_progress',
transactionMined: status.transactionMined,
proofSetLive: status.proofSetLive,
elapsedMs: status.elapsedMs
}, 'Proof set creation progress')
}
}
})
logger.info({
event: 'synapse.storage.created',
proofSetId: storage.proofSetId,
storageProvider: storage.storageProvider
}, 'Storage service created successfully')
// Store instances
synapseInstance = synapse
storageInstance = storage
return { synapse, storage }
} catch (error) {
logger.error({
event: 'synapse.init.failed',
error
}, 'Failed to initialize Synapse')
throw error
}
}
/**
* Get the initialized Synapse service
*/
export function getSynapseService (): SynapseService | null {
if (synapseInstance == null || storageInstance == null) {
return null
}
return {
synapse: synapseInstance,
storage: storageInstance
}
}
/**
* Upload data to Filecoin using Synapse
*/
export async function uploadToSynapse (
data: Uint8Array,
pinId: string,
logger: Logger
): Promise<{
commp: string
rootId: number
proofSetId: string
}> {
const service = getSynapseService()
if (service == null) {
throw new Error('Synapse service not initialized')
}
logger.info({
event: 'synapse.upload.start',
pinId,
size: data.length
}, 'Starting Synapse upload')
const uploadCallbacks: UploadCallbacks = {
onUploadComplete: (commp) => {
logger.info({
event: 'synapse.upload.piece_uploaded',
pinId,
commp: commp.toString()
}, 'Upload to PDP server complete')
},
onRootAdded: (transaction) => {
if (transaction != null) {
logger.info({
event: 'synapse.upload.root_added',
pinId,
txHash: transaction.hash
}, 'Root addition transaction submitted')
} else {
logger.info({
event: 'synapse.upload.root_added',
pinId
}, 'Root added to proof set')
}
},
onRootConfirmed: (rootIds) => {
logger.info({
event: 'synapse.upload.root_confirmed',
pinId,
rootIds
}, 'Root addition confirmed on-chain')
}
}
const uploadResult = await service.storage.upload(data, uploadCallbacks)
logger.info({
event: 'synapse.upload.complete',
pinId,
commp: uploadResult.commp.toString(),
rootId: uploadResult.rootId,
proofSetId: service.storage.proofSetId
}, 'Synapse upload completed successfully')
return {
commp: uploadResult.commp.toString(),
rootId: uploadResult.rootId ?? 0,
proofSetId: service.storage.proofSetId
}
}