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
1 change: 1 addition & 0 deletions apps/synapse-playground/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
"lucide-react": "^0.546.0",
"nanostores": "^1.0.1",
"next-themes": "^0.4.6",
"p-retry": "^7.1.0",
"react": "19.2.0",
"react-dom": "19.2.0",
"react-dropzone": "^14.3.8",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,13 @@ export function CreateDataSetDialog() {
cdn: boolean
}>({
values: {
provider: providers?.[2]?.providerId.toString() ?? '',
provider: providers?.[0]?.id.toString() ?? '',
cdn: false,
},
})

function onSubmit(values: { provider: string; cdn: boolean }) {
const provider = providers?.find((p) => p.providerId.toString() === values.provider)
const provider = providers?.find((p) => p.id.toString() === values.provider)
createDataSet({ provider: provider!, cdn: values.cdn })
}

Expand Down Expand Up @@ -94,8 +94,8 @@ export function CreateDataSetDialog() {
</FormControl>
<SelectContent>
{providers?.map((provider) => (
<SelectItem key={provider.providerId} value={provider.providerId.toString()}>
# {provider.providerId} {provider.name}
<SelectItem key={provider.id} value={provider.id.toString()}>
# {provider.id} {provider.name}
</SelectItem>
))}
</SelectContent>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import type { DataSetWithPieces, UseProvidersResult } from '@filoz/synapse-react'
import { CloudDownload, FileAudio, FileCode, FilePlay, FileText, Globe, Info } from 'lucide-react'
import { useDeletePiece } from '@filoz/synapse-react'
import { CloudDownload, FileAudio, FileCode, FilePlay, FileText, Globe, Info, Trash } from 'lucide-react'
import { useState } from 'react'
import { toast } from 'sonner'
import { toastError } from '@/lib/utils.ts'
import { ButtonLoading } from '../custom-ui/button-loading.tsx'
import { ExplorerLink } from '../explorer-link.tsx'
import { PDPDatasetLink, PDPPieceLink, PDPProviderLink } from '../pdp-link.tsx'
import { Avatar, AvatarFallback, AvatarImage } from '../ui/avatar.tsx'
import { Button } from '../ui/button.tsx'
Expand All @@ -15,7 +21,7 @@ export function DataSetsSection({
dataSets?: DataSetWithPieces[]
providers?: UseProvidersResult
}) {
const providerWithDataSets = providers?.filter((p) => dataSets?.some((d) => d.providerId === p.providerId))
const providerWithDataSets = providers?.filter((p) => dataSets?.some((d) => d.providerId === p.id))

const imagesMimeTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp']
const videosMimeTypes = ['video/mp4', 'video/quicktime', 'video/webm']
Expand Down Expand Up @@ -45,6 +51,29 @@ export function DataSetsSection({
'application/x-toml',
]

const [deletingPiece, setDeletingPiece] = useState<bigint | null>(null)
const { mutate: deletePiece, isPending: isDeletingPiece } = useDeletePiece({
onHash: (hash) => {
toast.loading('Deleting piece...', {
description: <ExplorerLink hash={hash} />,
id: 'delete-piece',
})
},
mutation: {
onSuccess: () => {
toast.success('Piece deleted', {
id: 'delete-piece',
})
},
onError: (error) => {
toastError(error, 'delete-piece', 'Piece deletion failed')
},
onSettled: () => {
setDeletingPiece(null)
},
},
})

return providers ? (
<div>
<div className="flex flex-row gap-2 items-center justify-between">
Expand All @@ -57,16 +86,16 @@ export function DataSetsSection({

<div className="flex flex-col gap-2 mt-6">
{providerWithDataSets?.map((provider) => (
<div className="flex flex-col gap-4" key={provider.providerId}>
<div className="flex flex-col gap-4" key={provider.id}>
<h4 className="text-lg font-bold">
<PDPProviderLink address={provider.serviceProvider} name={provider.name} />
</h4>
{dataSets
?.filter((dataSet) => dataSet.providerId === provider.providerId)
?.filter((dataSet) => dataSet.providerId === provider.id)
.map((dataSet) => (
<div className="flex flex-col gap-2" key={dataSet.clientDataSetId}>
<p className="flex flex-row gap-2 items-center">
<PDPDatasetLink id={dataSet.pdpDatasetId.toString()} />
<PDPDatasetLink id={dataSet.dataSetId.toString()} />
<Tooltip>
<TooltipTrigger>
<Info className="w-4" />
Expand All @@ -93,7 +122,7 @@ export function DataSetsSection({
</p>

{dataSet.pieces.map((piece) => (
<Item key={`${piece.pieceId}-${dataSet.pdpDatasetId}`} size="default" variant="muted">
<Item key={`${piece.id}-${dataSet.dataSetId}`} size="default" variant="muted">
<ItemMedia
variant={
imagesMimeTypes.includes(piece.metadata.type)
Expand All @@ -105,10 +134,10 @@ export function DataSetsSection({
>
{imagesMimeTypes.includes(piece.metadata.type) ? (
<img
alt={piece.metadata.name || piece.pieceCid}
alt={piece.metadata.name || piece.cid.toString()}
className="object-cover"
height={48}
src={piece.pieceUrl}
src={piece.url}
width={48}
/>
) : videosMimeTypes.includes(piece.metadata.type) ? (
Expand All @@ -121,25 +150,39 @@ export function DataSetsSection({
<FileCode className="w-10" />
) : (
<Avatar className="size-10">
<AvatarImage src={piece.pieceUrl} />
<AvatarImage src={piece.url} />
<AvatarFallback>NA</AvatarFallback>
</Avatar>
)}
</ItemMedia>
<ItemContent>
<ItemTitle className="break-all">
<PDPPieceLink cid={piece.pieceCid} name={piece.metadata.name} />
<PDPPieceLink cid={piece.cid.toString()} name={piece.metadata.name} />
</ItemTitle>
<ItemDescription>{piece.metadata.type}</ItemDescription>
<ItemDescription>
{piece.metadata.type} {piece.id}
</ItemDescription>
</ItemContent>
<ItemActions>
<Button
onClick={() => {
window.open(piece.pieceUrl, '_blank')
window.open(piece.url, '_blank')
}}
>
<CloudDownload />
</Button>
<ButtonLoading
loading={isDeletingPiece && deletingPiece === piece.id}
onClick={async () => {
setDeletingPiece(piece.id)
deletePiece({
dataSet,
pieceId: piece.id,
})
}}
>
<Trash />
</ButtonLoading>
</ItemActions>
</Item>
))}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@ export function UploadsSection({

const providerWithDataSets = providers?.map((provider) => ({
...provider,
dataSets: dataSets?.filter((d) => d.providerId === provider.providerId),
dataSets: dataSets?.filter((d) => d.providerId === provider.id),
}))

useEffect(() => {
if (!dataSet && dataSets && dataSets.length > 0) {
setDataSet(dataSets[0].pdpDatasetId.toString())
setDataSet(dataSets[0].dataSetId.toString())
}
}, [dataSets])

Expand Down Expand Up @@ -86,11 +86,11 @@ export function UploadsSection({
</SelectTrigger>
<SelectContent>
{providerWithDataSets?.map((provider) => (
<SelectGroup key={provider.providerId}>
<SelectGroup key={provider.id}>
<SelectLabel>{provider.name}</SelectLabel>
{provider.dataSets?.map((dataSet) => (
<SelectItem key={dataSet.clientDataSetId} value={dataSet.pdpDatasetId.toString()}>
# {dataSet.pdpDatasetId} {dataSet.cdn ? 'CDN' : ''}
<SelectItem key={dataSet.dataSetId} value={dataSet.dataSetId.toString()}>
# {dataSet.dataSetId} {dataSet.cdn ? 'CDN' : ''}
</SelectItem>
))}
</SelectGroup>
Expand Down
2 changes: 1 addition & 1 deletion apps/synapse-playground/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export const config = createConfig({
transports: {
[mainnet.id]: http(),
[calibration.id]: http(undefined, {
batch: true,
batch: false,
}),
},
batch: {
Expand Down
2 changes: 1 addition & 1 deletion biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
"!**/components.d.ts",
"!**/mockServiceWorker.js",
"!**/abis/gen.ts",
"!packages/synapse-core/src/generated.ts"
"!packages/synapse-core/src/abis/generated.ts"
]
},
"vcs": {
Expand Down
2 changes: 1 addition & 1 deletion docs/src/content/docs/guides/rails-settlement.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Rails ensure reliable payments through a simple lockup mechanism:

When you create a data set (storage), the system calculates how much balance you need to maintain:

- **Formula**: `lockup = paymentRate × lockupPeriod` (e.g., 10 days worth of payments)
- **Formula**: `lockup = paymentRate × lockupPeriod` (e.g., 30 days worth of payments)
- **Example**: Storing 1 GiB costs ~0.0000565 USDFC/epoch, requiring ~1.63 USDFC minimum balance
- **Purpose**: This protects the service provider by ensuring you always have enough for the next payment period

Expand Down
3 changes: 3 additions & 0 deletions docs/src/content/docs/intro/components.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,9 @@ if (verification.dataSetLive) {
// Service provider operations
const isApproved = await warmStorageService.isProviderApproved(providerAddress)
const providers = await warmStorageService.getAllApprovedProviders()

// Top up CDN payment rails
await wamStorageService.topUpCDNPaymentRails(signer, dataSetId, cdnAmountToAdd, cacheMissAmountToAdd)
```

### Subgraph Service
Expand Down
25 changes: 25 additions & 0 deletions examples/cli/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"name": "synapse-cli",
"version": "1.0.0",
"description": "CLI for Synapse",
"type": "module",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"packageManager": "pnpm@10.19.0",
"dependencies": {
"@clack/prompts": "^0.11.0",
"@filoz/synapse-sdk": "workspace:^",
"@filoz/synapse-core": "workspace:^",
"cleye": "^1.3.4",
"conf": "^15.0.2",
"viem": "^2.38.4"
},
"devDependencies": {
"@types/node": "^24.9.1"
}
}
87 changes: 87 additions & 0 deletions examples/cli/src/commands/dataset-terminate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import * as p from '@clack/prompts'
import { calibration } from '@filoz/synapse-core/chains'
import { getDataSets, terminateDataSet } from '@filoz/synapse-core/warm-storage'
import { RPC_URLS, Synapse } from '@filoz/synapse-sdk'
import { type Command, command } from 'cleye'
import { createPublicClient, createWalletClient, type Hex, http } from 'viem'
import { privateKeyToAccount } from 'viem/accounts'
import { waitForTransactionReceipt } from 'viem/actions'
import config from '../config.ts'

const publicClient = createPublicClient({
chain: calibration,
transport: http(),
})

export const datasetTerminate: Command = command(
{
name: 'dataset-terminate',
description: 'Terminate a data set',
alias: 'dt',
help: {
description: 'Terminate a data set',
},
},
async (argv) => {
const privateKey = config.get('privateKey')
if (!privateKey) {
p.log.error('Private key not found')
p.outro('Please run `synapse init` to initialize the CLI')
return
}

const account = privateKeyToAccount(privateKey as Hex)
const client = createWalletClient({
account,
chain: calibration,
transport: http(),
})

const spinner = p.spinner()
spinner.start(`Fetching data sets...`)
try {
const dataSets = await getDataSets(publicClient, {
address: account.address,
})
spinner.stop(`Fetching data sets complete`)

const dataSetId = await p.select({
message: 'Pick a data set to terminate.',
options: dataSets
// .filter((dataSet) => dataSet.pdpEndEpoch === 0n)
.map((dataSet) => ({
value: dataSet.dataSetId.toString(),
label: `#${dataSet.dataSetId} - SP: #${dataSet.providerId} ${dataSet.pdp.serviceURL}`,
})),
})
if (p.isCancel(dataSetId)) {
p.cancel('Operation cancelled.')
process.exit(0)
}

spinner.start(`Terminating data set ${dataSetId}...`)
// const synapse = await Synapse.create({
// privateKey: privateKey as Hex,
// rpcURL: RPC_URLS.calibration.http,
// })

// const tx = await synapse.storage.terminateDataSet(Number(dataSetId))

const tx = await terminateDataSet(client, {
dataSetId: BigInt(dataSetId),
})

spinner.message(`Waiting for transaction to be mined...`)
await waitForTransactionReceipt(publicClient, {
hash: tx,
})

spinner.stop(`Data set terminated`)
} catch (error) {
spinner.stop()
console.error(error)
p.outro('Please try again')
return
}
}
)
Loading