1- /**
2- * helios-verifier.ts
3- *
4- * Experimental light-client verification layer using Helios (@a16z/helios).
5- * Only active on Ethereum mainnet (chainId 0x1).
6- */
71
82export interface VerificationResult {
93 verified : boolean ;
@@ -18,9 +12,11 @@ const DEFAULT_CONSENSUS_RPC = 'https://ethereum.operationsolarstorm.org';
1812const CHECKPOINT_FETCH_TIMEOUT_MS = 5_000 ;
1913
2014let heliosProvider : any = null ;
21- let initInProgress = false ;
15+ let initPromise : Promise < void > | null = null ;
16+ let syncPromise : Promise < void > | null = null ;
2217let isSynced = false ;
2318let currentExecutionRpc = '' ;
19+ let sessionId = 0 ;
2420
2521async function fetchFreshCheckpoint ( consensusRpc : string ) : Promise < string | undefined > {
2622 const url = `${ consensusRpc . replace ( / \/ $ / , '' ) } /eth/v1/beacon/headers/finalized` ;
@@ -44,39 +40,50 @@ export async function initHelios(
4440 executionRpc : string ,
4541 consensusRpc : string = DEFAULT_CONSENSUS_RPC ,
4642) : Promise < void > {
47- if ( initInProgress ) return ;
43+ if ( initPromise ) return initPromise ;
4844 if ( heliosProvider && currentExecutionRpc === executionRpc ) return ;
49- initInProgress = true ;
45+ const session = ++ sessionId ;
5046 heliosProvider = null ;
5147 isSynced = false ;
5248 currentExecutionRpc = executionRpc ;
53- try {
54- const checkpoint = await fetchFreshCheckpoint ( consensusRpc ) ;
55- const { createHeliosProvider } = await import ( '@a16z/helios' ) ;
56- const config : Record < string , string > = {
57- executionRpc,
58- consensusRpc,
59- network : 'mainnet' ,
60- } ;
61- if ( checkpoint ) config [ 'checkpoint' ] = checkpoint ;
62- heliosProvider = await createHeliosProvider ( config , 'ethereum' ) ;
63- heliosProvider
64- . waitSynced ( )
65- . then ( ( ) => { isSynced = true ; console . log ( '[helios-verifier] synced and ready' ) ; } )
66- . catch ( ( err : unknown ) => { console . warn ( '[helios-verifier] sync failed:' , err ) ; resetHelios ( ) ; } ) ;
67- } catch ( err ) {
68- console . warn ( '[helios-verifier] failed to initialise:' , err ) ;
69- heliosProvider = null ;
70- currentExecutionRpc = '' ;
71- } finally {
72- initInProgress = false ;
73- }
49+ initPromise = ( async ( ) => {
50+ try {
51+ const checkpoint = await fetchFreshCheckpoint ( consensusRpc ) ;
52+ const { createHeliosProvider } = await import ( '@a16z/helios' ) ;
53+ const config : Record < string , string > = {
54+ executionRpc,
55+ consensusRpc,
56+ network : 'mainnet' ,
57+ } ;
58+ if ( checkpoint ) config [ 'checkpoint' ] = checkpoint ;
59+ const provider = await createHeliosProvider ( config , 'ethereum' ) ;
60+ if ( session !== sessionId ) return ;
61+ heliosProvider = provider ;
62+ syncPromise = provider . waitSynced ( ) . then ( ( ) => {
63+ if ( session === sessionId && heliosProvider === provider ) {
64+ isSynced = true ;
65+ console . log ( '[helios-verifier] synced and ready' ) ;
66+ }
67+ } ) ;
68+ await syncPromise ;
69+ } catch ( err ) {
70+ if ( session === sessionId ) {
71+ console . warn ( '[helios-verifier] failed to initialise:' , err ) ;
72+ resetHelios ( ) ;
73+ }
74+ } finally {
75+ if ( session === sessionId ) initPromise = null ;
76+ }
77+ } ) ( ) ;
78+ return initPromise ;
7479}
7580
7681export function resetHelios ( ) : void {
82+ sessionId ++ ;
7783 heliosProvider = null ;
7884 isSynced = false ;
79- initInProgress = false ;
85+ initPromise = null ;
86+ syncPromise = null ;
8087 currentExecutionRpc = '' ;
8188}
8289
@@ -95,17 +102,18 @@ export async function verifyErc20Balance(
95102 contractAddress : string ,
96103 walletAddress : string ,
97104 rpcBalance : string ,
105+ blockTag : string ,
98106 chainId : string ,
99107 executionRpc : string ,
100108) : Promise < VerificationResult > {
101109 if ( chainId . toLowerCase ( ) !== MAINNET_CHAIN_ID ) return SKIP ;
102- if ( ! heliosProvider && ! initInProgress ) void initHelios ( executionRpc ) ;
110+ await initHelios ( executionRpc ) ;
103111 if ( ! isSynced || ! heliosProvider ) return SKIP ;
104112 let heliosBalanceHex : string ;
105113 try {
106114 heliosBalanceHex = ( await heliosProvider . request ( {
107115 method : 'eth_call' ,
108- params : [ { to : contractAddress , data : encodeBalanceOf ( walletAddress ) } , 'latest' ] ,
116+ params : [ { to : contractAddress , data : encodeBalanceOf ( walletAddress ) } , blockTag ] ,
109117 } ) ) as string ;
110118 } catch ( err ) {
111119 console . warn ( '[helios-verifier] eth_call failed:' , err ) ;
@@ -114,7 +122,12 @@ export async function verifyErc20Balance(
114122 const rpcValue = decodeUint256 ( rpcBalance ) ;
115123 const heliosValue = decodeUint256 ( heliosBalanceHex ) ;
116124 if ( rpcValue === heliosValue ) {
117- return { verified : true , tampered : false , message : 'Balance verified by Helios light client.' , provenBalance : heliosBalanceHex } ;
125+ return {
126+ verified : true ,
127+ tampered : false ,
128+ message : 'Balance verified by Helios light client.' ,
129+ provenBalance : heliosBalanceHex ,
130+ } ;
118131 }
119132 console . error ( `[helios-verifier] MISMATCH: RPC=${ rpcValue } Helios=${ heliosValue } ` ) ;
120133 return {
@@ -123,4 +136,4 @@ export async function verifyErc20Balance(
123136 message : `Your RPC provider returned ${ rpcValue . toString ( ) } but Helios proved the real balance is ${ heliosValue . toString ( ) } . Your RPC provider may be lying.` ,
124137 provenBalance : heliosBalanceHex ,
125138 } ;
126- }
139+ }
0 commit comments