@@ -10,6 +10,7 @@ import { SuiJsonRpcClient } from '@mysten/sui/jsonRpc';
1010import { Transaction } from '@mysten/sui/transactions' ;
1111import { marketplaceConfig , MIST_PER_SUI } from '@/config/marketplace' ;
1212import { getMarketplaceTarget } from '@/lib/marketplace' ;
13+ import { encryptFile , isSealAvailable , uint8ArrayToBase64 } from '@/lib/seal' ;
1314
1415// Type for Walrus write files flow
1516interface WalrusWriteFlow {
@@ -81,6 +82,7 @@ export default function MyDataPage() {
8182 previewSizeBytes : 1024 * 1024 ,
8283 } ) ;
8384
85+
8486 const flowRef = useRef < Awaited < ReturnType < typeof createFlow > > | null > ( null ) ;
8587
8688 const addLog = ( step : string , status : TransactionLog [ 'status' ] , message : string , details ?: string ) => {
@@ -151,20 +153,49 @@ export default function MyDataPage() {
151153 return ;
152154 }
153155
154- addLog ( 'encode' , 'processing' , 'Encoding file...' ) ;
155-
156- const fileBytes = await file . arrayBuffer ( ) ;
157- const uint8Array = new Uint8Array ( fileBytes ) ;
156+ // Step 0: Encrypt with Seal (if available)
157+ let dataToUpload : Uint8Array ;
158+ let encryptedObj : string ;
159+
160+ if ( isSealAvailable ( ) ) {
161+ addLog ( 'encode' , 'processing' , 'Encrypting with Seal (purchase-based access)...' ) ;
162+
163+ // Use seller address as policy ID - only buyers with PurchaseReceipt can decrypt
164+ console . log ( '[Seal] Encrypting file with purchase-based access' ) ;
165+
166+ try {
167+ const { encryptedBlob, policyId } = await encryptFile ( file , account . address ) ;
168+ const encryptedArrayBuffer = await encryptedBlob . arrayBuffer ( ) ;
169+ dataToUpload = new Uint8Array ( encryptedArrayBuffer ) ;
170+
171+ // Store policy ID as the encrypted object reference
172+ encryptedObj = policyId ;
173+ setEncryptedObject ( encryptedObj ) ;
174+
175+ addLog ( 'encode' , 'success' , 'File encrypted (only buyers can decrypt)' ) ;
176+ console . log ( '[Seal] Encryption complete, encrypted size:' , dataToUpload . length ) ;
177+ } catch ( sealError ) {
178+ console . error ( '[Seal] Encryption failed:' , sealError ) ;
179+ addLog ( 'encode' , 'error' , `Seal encryption failed: ${ sealError instanceof Error ? sealError . message : 'Unknown error' } ` ) ;
180+ setUploadError ( 'Failed to encrypt with Seal. Check console for details.' ) ;
181+ setIsProcessing ( false ) ;
182+ return ;
183+ }
184+ } else {
185+ // Fallback: no encryption (store raw data)
186+ addLog ( 'encode' , 'processing' , 'Encoding file (Seal not configured)...' ) ;
187+ const fileBytes = await file . arrayBuffer ( ) ;
188+ dataToUpload = new Uint8Array ( fileBytes ) ;
189+ encryptedObj = bytesToHex ( dataToUpload ) . slice ( 0 , 128 ) ;
190+ setEncryptedObject ( encryptedObj ) ;
191+ addLog ( 'encode' , 'success' , 'File encoded (no encryption)' ) ;
192+ }
158193
159- // Step 1: Create flow and encode
160- const flow = await createFlow ( uint8Array , file . name ) ;
194+ // Step 1: Create flow and encode with encrypted data
195+ const flow = await createFlow ( dataToUpload , file . name ) ;
161196 flowRef . current = flow ;
162197 await flow . encode ( ) ;
163- addLog ( 'encode' , 'success' , 'File encoded' ) ;
164-
165- // Generate encrypted object from file content (first 64 bytes as hex)
166- const encryptedObj = bytesToHex ( uint8Array ) . slice ( 0 , 128 ) ;
167- setEncryptedObject ( encryptedObj ) ;
198+ addLog ( 'encode' , 'success' , 'Walrus encoding complete' ) ;
168199
169200 // Step 2: Register blob on-chain
170201 setCurrentStep ( 'register' ) ;
@@ -471,7 +502,7 @@ export default function MyDataPage() {
471502 </ div >
472503 < span className = "material-symbols-outlined text-5xl text-gray-600" > dataset</ span >
473504 < span className = "absolute top-3 left-3 rounded bg-accent-lime text-ink px-2 py-0.5 text-[10px] font-bold border border-ink" > ACTIVE</ span >
474-
505+
475506 { /* Explorer Links */ }
476507 < div className = "absolute top-3 right-3 flex gap-1" >
477508 < a
@@ -482,10 +513,10 @@ export default function MyDataPage() {
482513 title = "View on SuiScan"
483514 >
484515 < svg className = "w-4 h-4" viewBox = "0 0 234 234" fill = "none" >
485- < 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" />
486- < 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" />
487- < 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" />
488- < 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" />
516+ < 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" />
517+ < 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" />
518+ < 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" />
519+ < 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" />
489520 </ svg >
490521 </ a >
491522 < a
@@ -496,22 +527,22 @@ export default function MyDataPage() {
496527 title = "View on SuiVision"
497528 >
498529 < svg className = "w-4 h-4" viewBox = "0 0 24 24" fill = "none" >
499- < 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" />
500- < 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" />
530+ < 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" />
531+ < 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" />
501532 </ svg >
502533 </ a >
503534 </ div >
504535 </ div >
505-
536+
506537 { /* Content */ }
507538 < div className = "p-4 flex flex-col flex-1" >
508539 < h3 className = "text-base font-bold text-ink mb-1 line-clamp-2" > { listing . name } </ h3 >
509-
540+
510541 { /* Object ID */ }
511542 < p className = "text-[10px] text-gray-400 font-mono mb-1 truncate" title = { listing . id } >
512543 ID: { listing . id . slice ( 0 , 8 ) } ...{ listing . id . slice ( - 6 ) }
513544 </ p >
514-
545+
515546 { /* Blob ID with Walrus link */ }
516547 < div className = "flex items-center gap-1 mb-3" >
517548 < p className = "text-[10px] text-gray-400 font-mono truncate flex-1" title = { listing . blobId } >
@@ -525,15 +556,15 @@ export default function MyDataPage() {
525556 title = "View on WalrusScan"
526557 >
527558 < svg className = "w-3 h-3" viewBox = "0 0 24 24" fill = "none" >
528- < circle cx = "12" cy = "10" r = "8" fill = "#36B5A8" />
529- < ellipse cx = "12" cy = "22" rx = "6" ry = "2" fill = "#36B5A8" opacity = "0.5" />
530- < circle cx = "9" cy = "8" r = "1.5" fill = "white" />
531- < circle cx = "15" cy = "8" r = "1.5" fill = "white" />
532- < path d = "M9 13C9 13 10.5 15 12 15C13.5 15 15 13 15 13" stroke = "white" strokeWidth = "1.5" strokeLinecap = "round" />
559+ < circle cx = "12" cy = "10" r = "8" fill = "#36B5A8" />
560+ < ellipse cx = "12" cy = "22" rx = "6" ry = "2" fill = "#36B5A8" opacity = "0.5" />
561+ < circle cx = "9" cy = "8" r = "1.5" fill = "white" />
562+ < circle cx = "15" cy = "8" r = "1.5" fill = "white" />
563+ < path d = "M9 13C9 13 10.5 15 12 15C13.5 15 15 13 15 13" stroke = "white" strokeWidth = "1.5" strokeLinecap = "round" />
533564 </ svg >
534565 </ a >
535566 </ div >
536-
567+
537568 { /* Stats */ }
538569 < div className = "grid grid-cols-2 gap-4 py-3 border-t border-gray-200" >
539570 < div >
@@ -545,7 +576,7 @@ export default function MyDataPage() {
545576 < p className = "text-sm font-bold text-accent-lime" > { formatSize ( Number ( listing . totalSize ) ) } </ p >
546577 </ div >
547578 </ div >
548-
579+
549580 { /* Actions */ }
550581 < div className = "flex gap-2 mt-auto pt-3 border-t border-gray-200" >
551582 < button className = "flex-1 h-9 rounded-lg border-2 border-ink bg-white text-ink text-sm font-bold hover:bg-gray-50 transition-colors" >
@@ -725,6 +756,28 @@ export default function MyDataPage() {
725756 < span > { formatSize ( totalSizeBytes ) } </ span >
726757 </ div >
727758 </ div >
759+
760+ { /* Seal Encryption Notice */ }
761+ < div className = "pt-4 border-t border-gray-200" >
762+ < label className = "block text-sm font-bold text-gray-500 uppercase mb-2 flex items-center gap-2" >
763+ < span className = "material-symbols-outlined text-base" > lock</ span >
764+ Purchase-Based Encryption (Seal)
765+ </ label >
766+ < p className = "text-xs text-gray-500 mb-2" >
767+ Data is encrypted with Seal threshold encryption. Only users who purchase this dataset will be able to decrypt it.
768+ </ p >
769+ { ! isSealAvailable ( ) && (
770+ < p className = "text-xs text-amber-600 bg-amber-50 p-2 rounded-lg border border-amber-200" >
771+ ⚠️ Seal not configured. Set NEXT_PUBLIC_PACKAGE_ID to enable encryption.
772+ </ p >
773+ ) }
774+ { isSealAvailable ( ) && (
775+ < p className = "text-xs text-green-600 bg-green-50 p-2 rounded-lg border border-green-200 flex items-center gap-1" >
776+ < span className = "material-symbols-outlined text-sm" > check_circle</ span >
777+ Seal encryption enabled - data will be protected
778+ </ p >
779+ ) }
780+ </ div >
728781 </ div >
729782
730783 < div className = "space-y-4" >
0 commit comments