@@ -192,16 +192,21 @@ function App() {
192192 const [ activeStep , setActiveStep ] = useState < PipelineStep > ( 'idle' )
193193 const [ running , setRunning ] = useState ( false )
194194 const [ completedSteps , setCompletedSteps ] = useState < Set < PipelineStep > > ( new Set ( ) )
195+ const [ escrowBal , setEscrowBal ] = useState < string > ( 'β' )
196+ const [ workerBal , setWorkerBal ] = useState < string > ( 'β' )
197+ const [ jobId , setJobId ] = useState < number > ( ( ) => Math . floor ( Math . random ( ) * 2 ** 32 ) )
195198 const eventIdRef = useRef ( 0 )
196199 const eventsEndRef = useRef < HTMLDivElement > ( null )
197200
198201 const jobPDA = useMemo ( ( ) => {
199202 if ( ! publicKey ) return null
203+ const idBuf = Buffer . alloc ( 8 )
204+ idBuf . writeBigUInt64LE ( BigInt ( jobId ) )
200205 return PublicKey . findProgramAddressSync (
201- [ Buffer . from ( 'job' ) , publicKey . toBuffer ( ) ] ,
206+ [ Buffer . from ( 'job' ) , publicKey . toBuffer ( ) , idBuf ] ,
202207 PROGRAM_ID
203208 ) [ 0 ]
204- } , [ publicKey ] )
209+ } , [ publicKey , jobId ] )
205210
206211 const archivePDA = useMemo ( ( ) => {
207212 if ( ! jobPDA ) return null
@@ -211,6 +216,18 @@ function App() {
211216 ) [ 0 ]
212217 } , [ jobPDA ] )
213218
219+ const refreshEscrowBalances = useCallback ( async ( ) => {
220+ if ( ! jobPDA ) return
221+ try {
222+ const pdaLamports = await connection . getBalance ( jobPDA )
223+ setEscrowBal ( ( pdaLamports / LAMPORTS_PER_SOL ) . toFixed ( 4 ) )
224+ } catch { setEscrowBal ( 'β' ) }
225+ try {
226+ const burnerLamports = await connection . getBalance ( erBurner . publicKey )
227+ setWorkerBal ( ( burnerLamports / LAMPORTS_PER_SOL ) . toFixed ( 4 ) )
228+ } catch { setWorkerBal ( 'β' ) }
229+ } , [ jobPDA , connection ] )
230+
214231 const addEvent = useCallback ( ( label : string , type : EventEntry [ 'type' ] , extra ?: Partial < EventEntry > ) => {
215232 const entry : EventEntry = {
216233 id : ++ eventIdRef . current ,
@@ -325,6 +342,7 @@ function App() {
325342
326343 const tx = await program . methods
327344 . initializeJob (
345+ new anchor . BN ( jobId ) ,
328346 new anchor . BN ( 0.1 * LAMPORTS_PER_SOL ) ,
329347 new anchor . BN ( Math . floor ( Date . now ( ) / 1000 ) + 3600 ) ,
330348 randomHash ( )
@@ -333,7 +351,8 @@ function App() {
333351 . transaction ( )
334352
335353 const sig = await sendTx ( connection , tx )
336- addEvent ( `Job created on L1` , 'success' , { txHash : sig , ms : Date . now ( ) - start } )
354+ addEvent ( `Job #${ jobId } created β 0.1 SOL escrowed` , 'success' , { txHash : sig , ms : Date . now ( ) - start } )
355+ await refreshEscrowBalances ( )
337356 setCompletedSteps ( prev => new Set ( [ ...prev , 'init' ] ) )
338357 return true
339358 } catch ( e ) {
@@ -549,6 +568,7 @@ function App() {
549568
550569 const sig = await sendL1WithBurner ( tx )
551570 addEvent ( `π Stake locked in escrow` , 'success' , { txHash : sig , ms : Date . now ( ) - start } )
571+ await refreshEscrowBalances ( )
552572 setCompletedSteps ( prev => new Set ( [ ...prev , 'staking' ] ) )
553573 return true
554574 } catch ( e ) {
@@ -577,6 +597,8 @@ function App() {
577597
578598 const sig = await sendTx ( connection , tx )
579599 addEvent ( `β
Job PASSED β SOL transferred to worker` , 'success' , { txHash : sig , ms : Date . now ( ) - start } )
600+ await refreshEscrowBalances ( )
601+ await refreshBalance ( )
580602 setCompletedSteps ( prev => new Set ( [ ...prev , 'settling' ] ) )
581603 return true
582604 } catch ( e ) {
@@ -625,7 +647,7 @@ function App() {
625647 if ( ! delegateResult ) { setRunning ( false ) ; return }
626648 await new Promise ( r => setTimeout ( r , 1000 ) )
627649
628- const job = await program . account . job . fetch ( jobPDA )
650+ const job = await ( program . account as any ) . job . fetch ( jobPDA )
629651 const status = job . status as number
630652 let erEndpoint : string | null = null
631653
@@ -758,7 +780,7 @@ function App() {
758780 </ div >
759781 </ div >
760782 < p className = "tagline" >
761- Real-time Bounty Pipeline Β· Solana L1 β MagicBlock ER
783+ Trustless Bounty Pipeline Β· Escrow-Protected Β· Solana L1 β MagicBlock ER
762784 </ p >
763785 </ header >
764786
@@ -799,6 +821,43 @@ function App() {
799821 </ div >
800822 </ section >
801823
824+ { /* Escrow Visualization */ }
825+ < section className = "escrow-section" >
826+ < h3 className = "section-heading" > π° Escrow Flow</ h3 >
827+ < div className = "escrow-flow" >
828+ < div className = { `escrow-account ${ activeStep === 'init' ? 'escrow-active' : '' } ` } >
829+ < div className = "escrow-icon" > π€</ div >
830+ < div className = "escrow-label" > Poster</ div >
831+ < div className = "escrow-balance" > { balanceSol } SOL</ div >
832+ < div className = "escrow-sublabel" > Pays reward</ div >
833+ </ div >
834+ < div className = { `escrow-arrow ${ completedSteps . has ( 'init' ) ? 'arrow-active' : '' } ` } >
835+ < span className = "arrow-label" > reward β</ span >
836+ < div className = "arrow-line" />
837+ </ div >
838+ < div className = { `escrow-account escrow-pda ${ ( activeStep === 'staking' || activeStep === 'settling' ) ? 'escrow-active' : '' } ` } >
839+ < div className = "escrow-icon" > π</ div >
840+ < div className = "escrow-label" > Job PDA</ div >
841+ < div className = "escrow-balance accent" > { escrowBal } SOL</ div >
842+ < div className = "escrow-sublabel" > Escrow vault</ div >
843+ </ div >
844+ < div className = { `escrow-arrow ${ completedSteps . has ( 'settling' ) ? 'arrow-active' : '' } ` } >
845+ < span className = "arrow-label" > β payout</ span >
846+ < div className = "arrow-line" />
847+ </ div >
848+ < div className = { `escrow-account ${ activeStep === 'staking' ? 'escrow-active' : '' } ` } >
849+ < div className = "escrow-icon" > π€</ div >
850+ < div className = "escrow-label" > Worker</ div >
851+ < div className = "escrow-balance" > { workerBal } SOL</ div >
852+ < div className = "escrow-sublabel" > Stakes & earns </ div >
853+ </ div >
854+ </ div >
855+ < div className = "escrow-legend" >
856+ < span className = "legend-item" > < span className = "legend-dot l1" /> On-chain escrow protects both parties</ span >
857+ { activeStep === 'complete' && < span className = "legend-item success" > β
All SOL transfers verified on L1</ span > }
858+ </ div >
859+ </ section >
860+
802861 { /* Controls + Event Stream */ }
803862 < div className = "bottom-row" >
804863 { /* Controls */ }
@@ -819,27 +878,38 @@ function App() {
819878 < span className = "kv-label" > balance</ span >
820879 < span className = "kv-val accent" > { balanceSol } SOL</ span >
821880 </ div >
881+ < div className = "kv" >
882+ < span className = "kv-label" > job ID</ span >
883+ < code className = "kv-val dim" > #{ jobId } </ code >
884+ </ div >
822885 < div className = "kv" >
823886 < span className = "kv-label" > job PDA</ span >
824887 < code className = "kv-val dim" > { jobPDA ?. toBase58 ( ) . slice ( 0 , 12 ) } ...</ code >
825888 </ div >
889+ < div className = "kv" >
890+ < span className = "kv-label" > escrow</ span >
891+ < span className = "kv-val accent" > { escrowBal } SOL</ span >
892+ </ div >
826893 </ div >
827894
828895 < div className = "btn-group" >
829896 < button className = "btn btn-run" onClick = { runFullDemo } disabled = { running } >
830897 { running ? 'β³ Running...' : 'βΆ Run Full Lifecycle' }
831898 </ button >
899+ < button className = "btn btn-sm btn-new" onClick = { ( ) => { setJobId ( Math . floor ( Math . random ( ) * 2 ** 32 ) ) ; setEvents ( [ ] ) ; setCompletedSteps ( new Set ( ) ) ; setActiveStep ( 'idle' ) } } disabled = { running } >
900+ π New Job
901+ </ button >
832902 < button className = "btn btn-sm" onClick = { stepAirdrop } disabled = { running } >
833903 π§ Airdrop
834904 </ button >
835- < button className = "btn btn-sm" onClick = { refreshBalance } disabled = { running } >
905+ < button className = "btn btn-sm" onClick = { ( ) => { refreshBalance ( ) ; refreshEscrowBalances ( ) } } disabled = { running } >
836906 π Refresh
837907 </ button >
838908 </ div >
839909
840910 { activeStep === 'complete' && (
841911 < div className = "complete-banner" >
842- π Pipeline Complete β All 7 steps executed successfully
912+ π Pipeline Complete β All { PIPELINE_ORDER . length - 2 } steps with real SOL escrow
843913 </ div >
844914 ) }
845915 </ >
0 commit comments