Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
512efbb
fix(sdk): eliminate critical type safety violations (Phase 1)
tnunamak Oct 31, 2025
f306924
fix(sdk): complete Phase 1 type safety improvements
tnunamak Nov 1, 2025
fd99c37
test(sdk): add comprehensive tests for critical modules (Phase 5 earl…
tnunamak Nov 1, 2025
ba94827
test(sdk): add tests for Download, TypeGuards, and Wallet utilities
tnunamak Nov 1, 2025
a82ad42
fix(sdk): remove non-null assertions with proper runtime guards
tnunamak Nov 1, 2025
eb76051
fix(sdk): add Redis and relayer handler type safety
tnunamak Nov 1, 2025
b8dad05
test(sdk): add comprehensive tests for SubgraphMetaCache, ParseTransa…
tnunamak Nov 1, 2025
52482b3
test(sdk): add comprehensive tests for schema validation, grant valid…
tnunamak Nov 1, 2025
9dcfb88
test(sdk): add comprehensive tests for IPFS, crypto utilities, and en…
tnunamak Nov 1, 2025
3dc390b
test(sdk): add comprehensive tests for formatters and grants utilitie…
tnunamak Nov 1, 2025
b3ddfae
test(sdk): add comprehensive encryption utility tests (Batch 6)
tnunamak Nov 1, 2025
c2cd6c4
test(sdk): fix TypeScript errors and update event names in tests
tnunamak Nov 5, 2025
5b83cad
style(sdk): fix ESLint warnings in test files
tnunamak Nov 5, 2025
c4ba505
fix(console): resolve RedisAtomicStore type incompatibility
tnunamak Nov 5, 2025
5ba74c9
fix(sdk): restore relayer polling and value forwarding regressions
tnunamak Nov 5, 2025
971bc0d
chore: remove AUDIT_REPORT.md from PR
tnunamak Nov 5, 2025
76d65f4
fix(types): improve type safety pragmatically per TYPES_GUIDE.md
tnunamak Nov 5, 2025
3695406
fix: make NEXT_PUBLIC_PERSONAL_SERVER_BASE_URL optional
tnunamak Nov 20, 2025
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
213 changes: 213 additions & 0 deletions examples/vana-console/src/app/(dashboard)/dlp-operations/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
"use client";

import React, { useState } from "react";
import { useChainId } from "wagmi";
import {
Card,
CardHeader,
CardBody,
Button,
Input,
Table,
TableHeader,
TableColumn,
TableBody,
TableRow,
TableCell,
Spinner,
} from "@heroui/react";
import { DollarSign, Plus, RefreshCw } from "lucide-react";
import type { RuntimePermission } from "@opendatalabs/vana-sdk/browser";
import { useVana } from "@/providers/VanaProvider";
import { CreateRuntimePermissionModal } from "@/components/ui/CreateRuntimePermissionModal";

/**
* DLP Operations page - Monetize dataset access via Vana Runtime
*
* This page allows DLP operators to create and manage runtime permissions
* for their datasets. Data consumers can request access, pay for operations,
* and execute tasks on encrypted data through the Vana Runtime TEE environment.
*/
export default function DLPOperationsPage() {
const chainId = useChainId();
const { vana } = useVana();

// State
const [datasetIdInput, setDatasetIdInput] = useState<string>("");
const [selectedDatasetId, setSelectedDatasetId] = useState<bigint | null>(
null,
);
const [permissions, setPermissions] = useState<RuntimePermission[]>([]);
const [isLoadingPermissions, setIsLoadingPermissions] = useState(false);
const [isModalOpen, setIsModalOpen] = useState(false);
const [error, setError] = useState<string | null>(null);

// Load permissions for selected dataset
const loadPermissions = async () => {
if (!vana || !datasetIdInput) {
setError("Please enter a dataset ID");
return;
}

setIsLoadingPermissions(true);
setError(null);

try {
const datasetId = BigInt(datasetIdInput);
setSelectedDatasetId(datasetId);

// TODO: Re-enable when VanaRuntimePermissions contract is deployed and SDK is updated
// const permissionIds =
// await vana.runtimePermissions.getDatasetPermissions(datasetId);
// const perms = await Promise.all(
// permissionIds.map((id: bigint) => vana.runtimePermissions.getPermission(id)),
// );

// Stub implementation until contract is deployed
const perms: RuntimePermission[] = [];

setPermissions(perms);
} catch (err) {
console.error("Failed to load permissions:", err);
setError(
err instanceof Error ? err.message : "Failed to load permissions",
);
setPermissions([]);
} finally {
setIsLoadingPermissions(false);
}
};

// Handle permission created
const handlePermissionCreated = () => {
setIsModalOpen(false);
void loadPermissions(); // Refresh the list
};

return (
<div className="max-w-6xl mx-auto p-6 space-y-6">
{/* Header */}
<div className="mb-8">
<h1 className="text-3xl font-bold text-foreground mb-2">
DLP Operations
</h1>
<p className="text-lg text-default-600">
Monetize dataset access via Vana Runtime
</p>
</div>

{/* Dataset Selector */}
<Card>
<CardHeader>
<div className="flex items-center gap-2">
<DollarSign className="h-5 w-5" />
<h3 className="text-lg font-semibold">Select Dataset</h3>
</div>
</CardHeader>
<CardBody>
<div className="flex gap-2">
<Input
type="number"
label="Dataset ID"
placeholder="Enter your dataset ID"
value={datasetIdInput}
onChange={(e) => setDatasetIdInput(e.target.value)}
onKeyDown={(e) => {
if (e.key === "Enter") {
void loadPermissions();
}
}}
className="flex-1"
/>
<Button
color="primary"
startContent={<RefreshCw className="h-4 w-4" />}
onPress={() => {
void loadPermissions();
}}
isLoading={isLoadingPermissions}
>
Load Permissions
</Button>
</div>
{error && (
<p className="text-danger text-sm mt-2">{error}</p>
)}
</CardBody>
</Card>

{/* Permissions Table */}
{selectedDatasetId !== null && (
<Card>
<CardHeader className="flex justify-between items-center">
<div className="flex items-center gap-2">
<h3 className="text-lg font-semibold">
Runtime Permissions for Dataset #{selectedDatasetId.toString()}
</h3>
</div>
<Button
color="primary"
startContent={<Plus className="h-4 w-4" />}
onPress={() => setIsModalOpen(true)}
isDisabled={!vana}
>
Create Permission
</Button>
</CardHeader>
<CardBody>
{isLoadingPermissions ? (
<div className="flex justify-center items-center p-8">
<Spinner size="lg" />
</div>
) : permissions.length > 0 ? (
<Table aria-label="Runtime permissions table" removeWrapper>
<TableHeader>
<TableColumn>Grantee ID</TableColumn>
<TableColumn>Dataset ID</TableColumn>
<TableColumn>Grant URL</TableColumn>
<TableColumn>Start Block</TableColumn>
<TableColumn>End Block</TableColumn>
</TableHeader>
<TableBody>
{permissions.map((permission, index) => (
<TableRow key={index}>
<TableCell className="font-mono text-sm">
{permission.granteeId.toString()}
</TableCell>
<TableCell className="font-mono text-sm">
{permission.datasetId.toString()}
</TableCell>
<TableCell className="font-mono text-xs max-w-xs truncate">
{permission.grant}
</TableCell>
<TableCell>{permission.startBlock.toString()}</TableCell>
<TableCell>{permission.endBlock.toString()}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
) : (
<div className="text-center p-8">
<p className="text-default-600">
No permissions found for this dataset.
</p>
<p className="text-sm text-default-500 mt-2">
Create a permission to allow data consumers to access your
dataset.
</p>
</div>
)}
</CardBody>
</Card>
)}

{/* Create Permission Modal */}
<CreateRuntimePermissionModal
isOpen={isModalOpen}
onClose={() => setIsModalOpen(false)}
datasetId={selectedDatasetId}
onSuccess={handlePermissionCreated}
/>
</div>
);
}
9 changes: 8 additions & 1 deletion examples/vana-console/src/app/api/relay/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ import {
handleRelayerOperation,
RedisAtomicStore,
type UnifiedRelayerRequest,
type IRedisClient,
vanaMainnet,
mokshaTestnet,
PinataStorage,
} from "@opendatalabs/vana-sdk/node";
import { createWalletClient, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { RedisOperationStore } from "@/lib/operationStore";
import Redis from "ioredis";

export async function POST(request: NextRequest) {
try {
Expand Down Expand Up @@ -43,8 +45,13 @@ export async function POST(request: NextRequest) {
redis: process.env.REDIS_URL,
});

// Create Redis client instance for atomic store
// ioredis.Redis is structurally compatible with IRedisClient (duck typing).
// Type assertion required because Redis has additional method overloads that IRedisClient doesn't declare.
// Runtime validation in RedisAtomicStore constructor ensures compatibility.
const redisClient = new Redis(process.env.REDIS_URL);
atomicStore = new RedisAtomicStore({
redis: process.env.REDIS_URL,
redis: redisClient as unknown as IRedisClient,
});

console.info(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,11 @@ export async function POST(request: NextRequest) {
// Use the SDK's chain configuration approach
const defaultPersonalServerUrl =
process.env.NEXT_PUBLIC_PERSONAL_SERVER_BASE_URL;
if (!defaultPersonalServerUrl) {
throw new Error(
"NEXT_PUBLIC_PERSONAL_SERVER_BASE_URL environment variable is required",
);
}

const vana = Vana({
chainId,
account: applicationAccount,
defaultPersonalServerUrl,
...(defaultPersonalServerUrl && { defaultPersonalServerUrl }),
});

console.debug("🔍 [Poll] Polling operation:", {
Expand Down
7 changes: 1 addition & 6 deletions examples/vana-console/src/app/api/trusted-server/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,11 @@ export async function POST(request: NextRequest) {
// Use the SDK's chain configuration approach
const defaultPersonalServerUrl =
process.env.NEXT_PUBLIC_PERSONAL_SERVER_BASE_URL;
if (!defaultPersonalServerUrl) {
throw new Error(
"NEXT_PUBLIC_PERSONAL_SERVER_BASE_URL environment variable is required",
);
}

const vana = Vana({
chainId,
account: applicationAccount,
defaultPersonalServerUrl,
...(defaultPersonalServerUrl && { defaultPersonalServerUrl }),
});

console.debug("🔍 Debug - vana configured with:", {
Expand Down
9 changes: 8 additions & 1 deletion examples/vana-console/src/app/api/worker/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ import { NextResponse } from "next/server";
import {
Vana,
RedisAtomicStore,
type IRedisClient,
handleRelayerOperation,
mokshaTestnet,
vanaMainnet,
} from "@opendatalabs/vana-sdk/node";
import { createPublicClient, createWalletClient, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { RedisOperationStore } from "@/lib/operationStore";
import Redis from "ioredis";

// Configuration from environment
const MAX_RETRIES = parseInt(process.env.WORKER_MAX_RETRIES ?? "3");
Expand Down Expand Up @@ -51,8 +53,13 @@ export async function GET(request: NextRequest) {
redis: process.env.REDIS_URL!,
});

// Create Redis client instance for atomic store
// ioredis.Redis is structurally compatible with IRedisClient (duck typing).
// Type assertion required because Redis has additional method overloads that IRedisClient doesn't declare.
// Runtime validation in RedisAtomicStore constructor ensures compatibility.
const redisClient = new Redis(process.env.REDIS_URL!);
const atomicStore = new RedisAtomicStore({
redis: process.env.REDIS_URL!,
redis: redisClient as unknown as IRedisClient,
});

// Update worker heartbeat
Expand Down
9 changes: 8 additions & 1 deletion examples/vana-console/src/components/SidebarNavigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import React from "react";
import Link from "next/link";
import { usePathname } from "next/navigation";
import { Button } from "@heroui/react";
import { Database, Settings, Zap, FileCode } from "lucide-react";
import { Database, Settings, Zap, FileCode, DollarSign } from "lucide-react";

/**
* Represents a navigation view in the sidebar
Expand Down Expand Up @@ -55,6 +55,13 @@ const navigationViews: NavigationView[] = [
icon: FileCode,
description: "View network contracts",
},
{
id: "dlp-operations",
href: "/dlp-operations",
label: "DLP Operations",
icon: DollarSign,
description: "Monetize dataset access",
},
{
id: "developer-tools",
href: "/developer-tools",
Expand Down
Loading