Skip to content

Commit 05270b6

Browse files
committed
upload seal, fix my-data/page
2 parents 85f5114 + bcca766 commit 05270b6

4 files changed

Lines changed: 151 additions & 49 deletions

File tree

app/marketplace/layout.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export default function MarketplaceLayout({
1212
<MarketplaceFilterProvider>
1313
<div className="flex min-h-screen w-full flex-row overflow-hidden font-sans antialiased selection:bg-accent-lime selection:text-ink">
1414
<MarketplaceSidebar />
15-
<main className="flex-1 flex flex-col h-screen overflow-hidden relative bg-background-light">
15+
<main className="flex-1 flex flex-col h-screen overflow-y-auto relative bg-background-light">
1616
{children}
1717
</main>
1818
</div>

app/marketplace/my-data/page.tsx

Lines changed: 42 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,15 @@ import { SuiJsonRpcClient } from '@mysten/sui/jsonRpc';
1010
import { Transaction } from '@mysten/sui/transactions';
1111
import { marketplaceConfig, MIST_PER_SUI } from '@/config/marketplace';
1212
import { getMarketplaceTarget } from '@/lib/marketplace';
13+
import { encryptForMarketplace } from '@/lib/seal';
1314

14-
// Type for Walrus write files flow
15-
interface WalrusWriteFlow {
15+
// Type for Walrus write blob flow (raw bytes, no file metadata)
16+
interface WalrusBlobFlow {
1617
encode: () => Promise<void>;
1718
register: (opts: { epochs: number; owner: string; deletable: boolean }) => Transaction;
1819
upload: (opts: { digest: string }) => Promise<void>;
1920
certify: () => Transaction;
20-
listFiles: () => Promise<Array<{ blobId?: string; id?: string }>>;
21+
getBlob: () => Promise<{ blobId: string; blobObject: unknown }>;
2122
}
2223

2324
const walrusModule = {
@@ -93,9 +94,8 @@ export default function MyDataPage() {
9394
}]);
9495
};
9596

96-
const createFlow = useCallback(async (data: Uint8Array, identifier: string) => {
97+
const createFlow = useCallback(async (data: Uint8Array, _identifier: string) => {
9798
const walrus = await getWalrus();
98-
const { WalrusFile } = await import('@mysten/walrus');
9999

100100
const client = new SuiJsonRpcClient({
101101
url: getFullnodeUrl('testnet'),
@@ -107,15 +107,11 @@ export default function MyDataPage() {
107107
sendTip: { max: 1_000_000 },
108108
},
109109
})
110-
) as unknown as { walrus: { writeFilesFlow: (opts: { files: unknown[] }) => WalrusWriteFlow } };
110+
) as unknown as { walrus: { writeBlobFlow: (opts: { blob: Uint8Array }) => WalrusBlobFlow } };
111111

112-
const walrusFile = WalrusFile.from({
113-
contents: data,
114-
identifier,
115-
});
116-
117-
return client.walrus.writeFilesFlow({
118-
files: [walrusFile],
112+
// Use writeBlobFlow for raw bytes (no file metadata wrapper)
113+
return client.walrus.writeBlobFlow({
114+
blob: data,
119115
});
120116
}, []);
121117

@@ -151,21 +147,41 @@ export default function MyDataPage() {
151147
return;
152148
}
153149

154-
addLog('encode', 'processing', 'Encoding file...');
150+
// Step 0: Read file and encrypt with Seal
151+
addLog('encode', 'processing', 'Encrypting file with Seal...');
155152

156153
const fileBytes = await file.arrayBuffer();
157154
const uint8Array = new Uint8Array(fileBytes);
158155

159-
// Step 1: Create flow and encode
160-
const flow = await createFlow(uint8Array, file.name);
161-
flowRef.current = flow;
162-
await flow.encode();
163-
addLog('encode', 'success', 'File encoded');
156+
console.log('=== SEAL ENCRYPTION START ===');
157+
console.log('Original file size:', uint8Array.length);
158+
159+
// Encrypt data using Seal before uploading to Walrus
160+
let dataToUpload: Uint8Array;
161+
let encryptedObj: string;
162+
163+
try {
164+
const encryptResult = await encryptForMarketplace(uint8Array);
165+
dataToUpload = encryptResult.encryptedBytes;
166+
encryptedObj = encryptResult.encryptedHex.slice(0, 128); // Store first 128 chars as reference
167+
168+
console.log('Encrypted data size:', dataToUpload.length);
169+
addLog('encode', 'success', 'File encrypted with Seal');
170+
} catch (encryptError) {
171+
console.warn('Seal encryption failed, uploading unencrypted:', encryptError);
172+
// Fallback: upload unencrypted if Seal fails
173+
dataToUpload = uint8Array;
174+
encryptedObj = bytesToHex(uint8Array).slice(0, 128);
175+
addLog('encode', 'success', 'File prepared (unencrypted fallback)');
176+
}
164177

165-
// Generate encrypted object from file content (first 64 bytes as hex)
166-
const encryptedObj = bytesToHex(uint8Array).slice(0, 128);
167178
setEncryptedObject(encryptedObj);
168179

180+
// Step 1: Create flow with encrypted data
181+
const flow = await createFlow(dataToUpload, file.name);
182+
flowRef.current = flow;
183+
await flow.encode();
184+
169185
// Step 2: Register blob on-chain
170186
setCurrentStep('register');
171187
addLog('register', 'processing', 'Registering blob on Sui...');
@@ -202,18 +218,18 @@ export default function MyDataPage() {
202218
onSuccess: async (certifyResult) => {
203219
addLog('certify', 'success', 'Blob certified', `TX: ${certifyResult.digest.slice(0, 16)}...`);
204220

205-
// Step 5: Get blobId from listFiles (only available after certify)
221+
// Step 5: Get blobId from getBlob (for raw blob upload)
206222
let walrusBlobId = '';
207223
try {
208-
const files = await flow.listFiles();
209-
console.log('[Walrus] Files:', files);
210-
walrusBlobId = files[0]?.blobId || files[0]?.id || '';
224+
const blobInfo = await flow.getBlob();
225+
console.log('[Walrus] Blob:', blobInfo);
226+
walrusBlobId = blobInfo.blobId || '';
211227
if (walrusBlobId) {
212228
setBlobId(walrusBlobId);
213229
addLog('complete', 'success', 'Blob ID obtained', `ID: ${walrusBlobId.slice(0, 20)}...`);
214230
}
215231
} catch (listError) {
216-
console.error('[Walrus] listFiles error:', listError);
232+
console.error('[Walrus] getBlob error:', listError);
217233
}
218234

219235
// Step 6: Create listing

app/marketplace/page.tsx

Lines changed: 107 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ export default function MarketplacePage() {
140140
</div>
141141
</header>
142142

143-
<div className="px-8 mt-6">
143+
<div className="px-8 mt-6 pb-12">
144144
<div className="flex items-center justify-between mb-6">
145145
<h2 className="text-xl font-bold tracking-tight text-ink flex items-center gap-2">
146146
All Listings <span className="text-gray-400 text-base font-normal ml-1">({validListings.length} items)</span>
@@ -165,11 +165,26 @@ export default function MarketplacePage() {
165165
{isLoading ? (
166166
<div className={`grid gap-6 ${viewMode === 'grid' ? 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4' : 'grid-cols-1'}`}>
167167
{[1, 2, 3, 4, 5, 6, 7, 8].map(i => (
168-
<div key={i} className="rounded-xl border-2 border-ink bg-white p-6 animate-pulse">
169-
<div className="h-6 bg-gray-200 rounded w-3/4 mb-4"></div>
170-
<div className="h-4 bg-gray-200 rounded w-full mb-2"></div>
171-
<div className="h-4 bg-gray-200 rounded w-2/3"></div>
172-
</div>
168+
<article key={i} className="flex flex-col rounded-xl border-2 border-ink bg-white overflow-hidden animate-pulse">
169+
<div className="h-36 bg-gray-200" />
170+
<div className="p-4 flex flex-col flex-1">
171+
<div className="h-5 bg-gray-200 rounded w-3/4 mb-2" />
172+
<div className="h-3 bg-gray-100 rounded w-1/2 mb-3" />
173+
<div className="grid grid-cols-2 gap-4 py-3 border-t border-gray-200">
174+
<div>
175+
<div className="h-2 bg-gray-100 rounded w-12 mb-1" />
176+
<div className="h-4 bg-gray-200 rounded w-16" />
177+
</div>
178+
<div>
179+
<div className="h-2 bg-gray-100 rounded w-12 mb-1" />
180+
<div className="h-4 bg-gray-200 rounded w-16" />
181+
</div>
182+
</div>
183+
<div className="flex gap-2 mt-auto pt-3 border-t border-gray-200">
184+
<div className="flex-1 h-9 bg-gray-200 rounded-lg" />
185+
</div>
186+
</div>
187+
</article>
173188
))}
174189
</div>
175190
) : validListings && validListings.length > 0 ? (
@@ -178,32 +193,102 @@ export default function MarketplacePage() {
178193
<article
179194
key={listing.id}
180195
onClick={() => handleViewListing(listing)}
181-
className={`flex flex-col rounded-xl border-2 border-ink bg-white p-4 shadow-hard-sm hover:shadow-hard transition-all cursor-pointer ${isOwner(listing) ? 'ring-2 ring-primary ring-offset-2' : ''
196+
className={`flex flex-col rounded-xl border-2 border-ink bg-white overflow-hidden cursor-pointer hover:shadow-hard transition-all ${isOwner(listing) ? 'ring-2 ring-primary ring-offset-2' : ''
182197
}`}
183198
>
184-
<div className="flex items-start justify-between mb-4">
185-
<div className="h-12 w-12 rounded-lg border-2 border-ink bg-accent-blue flex items-center justify-center">
186-
<span className="material-symbols-outlined text-white">dataset</span>
199+
{/* Image/Preview Area */}
200+
<div className="relative h-36 bg-linear-to-br from-gray-800 to-gray-900 flex items-center justify-center">
201+
<div className="absolute inset-0 opacity-20">
202+
<svg className="w-full h-full" viewBox="0 0 100 100" preserveAspectRatio="none">
203+
<path d="M0,50 Q25,30 50,50 T100,50" stroke="currentColor" strokeWidth="0.5" fill="none" className="text-accent-lime" />
204+
<path d="M0,60 Q25,40 50,60 T100,60" stroke="currentColor" strokeWidth="0.5" fill="none" className="text-accent-lime" />
205+
</svg>
187206
</div>
188-
<div className="flex flex-col items-end gap-1">
189-
<span className="rounded bg-green-100 text-green-800 px-2 py-1 text-[10px] font-bold border border-green-200">
190-
ACTIVE
191-
</span>
207+
<span className="material-symbols-outlined text-5xl text-gray-600">dataset</span>
208+
209+
<div className="absolute top-3 left-3 flex flex-col gap-1">
210+
<span className="rounded bg-accent-lime text-ink px-2 py-0.5 text-[10px] font-bold border border-ink">ACTIVE</span>
192211
{isOwner(listing) && (
193-
<span className="text-xs font-bold text-primary">Your Listing</span>
212+
<span className="rounded bg-primary text-white px-2 py-0.5 text-[10px] font-bold">YOUR LISTING</span>
194213
)}
195214
</div>
215+
216+
{/* Explorer Links */}
217+
<div className="absolute top-3 right-3 flex gap-1" onClick={(e) => e.stopPropagation()}>
218+
<a
219+
href={`https://suiscan.xyz/testnet/object/${listing.id}`}
220+
target="_blank"
221+
rel="noopener noreferrer"
222+
className="h-7 w-7 rounded bg-white/90 hover:bg-white flex items-center justify-center transition-colors"
223+
title="View on SuiScan"
224+
>
225+
<svg className="w-4 h-4" viewBox="0 0 234 234" fill="none">
226+
<path d="M0 100C0 65 0 47.5 6.8 33C12.7 21.3 22.3 11.8 34 5.8C47.3 0 64.7 0 99.5 0H133.8C168.6 0 186 0 199.3 6.8C211 12.8 220.6 22.3 226.5 34C233.3 47.4 233.3 64.9 233.3 99.8V134.2C233.3 169.1 233.3 186.6 226.5 199.9C220.6 211.6 211 221.2 199.3 227.2C186 234 168.6 234 133.8 234H99.5C64.7 234 47.3 234 34 227.2C22.3 221.2 12.7 211.7 6.8 200C0 186.6 0 169.1 0 134.2V100Z" fill="#4C72FF" />
227+
<path d="M177 87C178.7 85.9 180.8 85.6 182.4 86.3C183.2 86.6 183.9 87.1 184.3 87.8C184.7 88.5 185 89.4 184.9 90.2L181.4 148.2C181 155.7 178.2 163.4 173.6 170.4C160.4 190.4 133.2 200 112.8 191.8C107.1 189.5 102.5 186 99.2 181.7C100 181.8 100.8 181.7 101.5 181.7C122.4 181.7 143.5 170.3 155.1 152.7C160.7 144.1 164 134.7 164.6 125.6L166.5 93.3L177 87Z" fill="white" />
228+
<path d="M150 63.6C151.7 62.5 153.8 62.3 155.5 62.9C156.3 63.3 156.9 63.8 157.4 64.5C157.9 65.2 158.1 66.1 158 66.9L154.5 125C154 132.5 151.3 140.1 146.7 147.1C133.5 167.2 106.3 176.7 85.9 168.5C80.1 166.2 75.6 162.7 72.3 158.4C73.1 158.4 73.9 158.4 74.6 158.4C95.6 158.4 116.6 147.1 128.2 129.4C133.9 120.8 137.1 111.4 137.7 102.3L139.6 70L150 63.6Z" fill="white" />
229+
<path d="M123 40.3C124.7 39.2 126.8 39 128.5 39.6C129.2 39.9 129.9 40.5 130.3 41.2C130.8 41.9 131 42.7 130.9 43.5L127.4 101.6C127 109.1 124.2 116.7 119.6 123.7C106.4 143.8 79.2 153.3 58.8 145.1C38.5 136.9 32.7 114 45.9 93.9C50.5 86.9 57.1 80.7 64.6 76.1L123 40.3Z" fill="white" />
230+
</svg>
231+
</a>
232+
<a
233+
href={`https://testnet.suivision.xyz/object/${listing.id}`}
234+
target="_blank"
235+
rel="noopener noreferrer"
236+
className="h-7 w-7 rounded bg-white/90 hover:bg-white flex items-center justify-center transition-colors"
237+
title="View on SuiVision"
238+
>
239+
<svg className="w-4 h-4" viewBox="0 0 24 24" fill="none">
240+
<path d="M0 6C0 2.68629 2.68629 0 6 0H18C21.3137 0 24 2.68629 24 6V18C24 21.3137 21.3137 24 18 24H6C2.68629 24 0 21.3137 0 18V6Z" fill="#4DA2FF" />
241+
<path d="M6.99748 5.28362L6.99748 11.0148L8.71768 12.0008L6.99731 12.987L6.99731 18.7182L11.9972 21.584L16.9971 18.7182L16.9971 12.9866L15.2769 12.0007L16.9973 11.0147L16.9973 5.28308L11.997 2.41732L6.99748 5.28362ZM11.6464 3.42366L11.6464 7.94789L7.69961 10.2105L7.69961 5.68623L11.6464 3.42366ZM12.3482 20.5781L12.3482 16.0535L16.2954 13.7912L16.2954 18.3159L12.3482 20.5781ZM15.9441 13.1879L11.9973 15.4501L8.05048 13.1879L9.41994 12.4031L11.9973 13.8803L14.575 12.4031L15.9441 13.1879ZM11.9973 10.1208L9.41964 11.5982L8.05048 10.8134L11.9973 8.55113L15.9445 10.8134L14.575 11.5982L11.9973 10.1208Z" fill="white" />
242+
</svg>
243+
</a>
244+
</div>
196245
</div>
197246

198-
<h3 className="text-lg font-bold text-ink mb-1 truncate">{listing.name}</h3>
199-
<p className="text-sm text-gray-500 mb-4 line-clamp-2">{listing.description}</p>
247+
{/* Content */}
248+
<div className="p-4 flex flex-col flex-1">
249+
<h3 className="text-base font-bold text-ink mb-1 line-clamp-2">{listing.name}</h3>
250+
251+
{/* Object ID */}
252+
<p className="text-[10px] text-gray-400 font-mono mb-1 truncate" title={listing.id}>
253+
ID: {listing.id.slice(0, 8)}...{listing.id.slice(-6)}
254+
</p>
200255

201-
<div className="mt-auto">
202-
<div className="flex items-center justify-between mb-2">
203-
<span className="text-xs text-gray-400">{formatSize(Number(listing.totalSize))}</span>
204-
<span className="text-lg font-bold text-primary">{formatPrice(listing.price)}</span>
256+
{/* Blob ID with Walrus link */}
257+
<div className="flex items-center gap-1 mb-3" onClick={(e) => e.stopPropagation()}>
258+
<p className="text-[10px] text-gray-400 font-mono truncate flex-1" title={listing.blobId}>
259+
Blob: {listing.blobId.slice(0, 8)}...{listing.blobId.slice(-6)}
260+
</p>
261+
<a
262+
href={`https://walruscan.com/testnet/blob/${listing.blobId}`}
263+
target="_blank"
264+
rel="noopener noreferrer"
265+
className="h-5 w-5 rounded bg-gray-100 hover:bg-gray-200 flex items-center justify-center transition-colors shrink-0"
266+
title="View on WalrusScan"
267+
>
268+
<svg className="w-3 h-3" viewBox="0 0 24 24" fill="none">
269+
<circle cx="12" cy="10" r="8" fill="#36B5A8" />
270+
<ellipse cx="12" cy="22" rx="6" ry="2" fill="#36B5A8" opacity="0.5" />
271+
<circle cx="9" cy="8" r="1.5" fill="white" />
272+
<circle cx="15" cy="8" r="1.5" fill="white" />
273+
<path d="M9 13C9 13 10.5 15 12 15C13.5 15 15 13 15 13" stroke="white" strokeWidth="1.5" strokeLinecap="round" />
274+
</svg>
275+
</a>
205276
</div>
206-
<div className="flex items-center gap-1 text-xs text-gray-400">
277+
278+
{/* Stats */}
279+
<div className="grid grid-cols-2 gap-4 py-3 border-t border-gray-200">
280+
<div>
281+
<p className="text-[10px] text-gray-400 uppercase tracking-wide">Price</p>
282+
<p className="text-sm font-bold text-ink">{formatPrice(listing.price)}</p>
283+
</div>
284+
<div>
285+
<p className="text-[10px] text-gray-400 uppercase tracking-wide">Size</p>
286+
<p className="text-sm font-bold text-accent-lime">{formatSize(Number(listing.totalSize))}</p>
287+
</div>
288+
</div>
289+
290+
{/* Seller */}
291+
<div className="flex items-center gap-1 pt-3 border-t border-gray-200 text-xs text-gray-400">
207292
<span className="material-symbols-outlined text-sm">person</span>
208293
<span>{shortenAddress(listing.seller)}</span>
209294
</div>

bun.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)