@@ -398,3 +398,149 @@ async fn post_fork_state_proof_should_not_be_supported() {
398398 let result = state. contract_multiproof ( vec ! [ address] ) ;
399399 assert_matches ! ( result, Err ( ProviderError :: StateProofNotSupported ) ) ;
400400}
401+
402+ #[ tokio:: test( flavor = "multi_thread" ) ]
403+ async fn pre_fork_state_root ( ) {
404+ let starknet_client = StarknetClient :: new ( SEPOLIA_RPC_URL . try_into ( ) . unwrap ( ) ) ;
405+
406+ // always use the latest block number of the forked chain because most nodes may not support
407+ // proofs for too old blocks
408+ //
409+ // we take the previous block because there were some instances where the latest block was not
410+ // available or supported by the node.
411+ let latest_block_number = starknet_client. block_number ( ) . await . unwrap ( ) . block_number - 1 ;
412+ let provider_factory =
413+ ForkProviderFactory :: new_in_memory ( latest_block_number, starknet_client. clone ( ) ) ;
414+ let provider = provider_factory. provider ( ) ;
415+
416+ //////////////////////////////////////////////////////////////////////////////
417+ // latest state
418+ //////////////////////////////////////////////////////////////////////////////
419+
420+ // this will return the latest state exactly at the forked block
421+ let state = provider. latest ( ) . unwrap ( ) ;
422+
423+ //--------------------------------------------------------
424+ // classes root
425+
426+ let actual_classes_root = state. classes_root ( ) . unwrap ( ) ;
427+ let expected_classes_root = starknet_client
428+ . get_storage_proof ( latest_block_number. into ( ) , None , None , None )
429+ . await
430+ . map ( |res| res. global_roots . classes_tree_root )
431+ . unwrap ( ) ;
432+
433+ assert_eq ! ( actual_classes_root, expected_classes_root) ;
434+
435+ //--------------------------------------------------------
436+ // contracts root
437+
438+ let actual_contracts_root = state. contracts_root ( ) . unwrap ( ) ;
439+ let expected_contracts_root = starknet_client
440+ . get_storage_proof ( latest_block_number. into ( ) , None , None , None )
441+ . await
442+ . map ( |res| res. global_roots . contracts_tree_root )
443+ . unwrap ( ) ;
444+
445+ assert_eq ! ( actual_contracts_root, expected_contracts_root) ;
446+
447+ //--------------------------------------------------------
448+ // contract storage root
449+
450+ let contract1 = address ! ( "0x049D36570D4e46f48e99674bd3fcc84644DdD6b96F7C741B1562B82f9e004dC7" ) ; // Ether Token
451+ let contract2 = address ! ( "0x04718f5a0Fc34cC1AF16A1cdee98fFB20C31f5cD61D6Ab07201858f4287c938D" ) ; // Starknet Token
452+ let contract3 = address ! ( "0x053C91253BC9682c04929cA02ED00b3E423f6710D2ee7e0D5EBB06F3eCF368A8" ) ; // USDC Token
453+
454+ let actual_contract1_root = state. storage_root ( contract1) . unwrap ( ) . unwrap ( ) ;
455+ let actual_contract2_root = state. storage_root ( contract2) . unwrap ( ) . unwrap ( ) ;
456+ let actual_contract3_root = state. storage_root ( contract3) . unwrap ( ) . unwrap ( ) ;
457+
458+ let ( expected_contract1_root, expected_contract2_root, expected_contract3_root) =
459+ starknet_client
460+ . get_storage_proof (
461+ latest_block_number. into ( ) ,
462+ None ,
463+ Some ( vec ! [ contract1, contract2, contract3] ) ,
464+ None ,
465+ )
466+ . await
467+ . map ( |res| {
468+ (
469+ // the leave must be ordered based on the order of the contracts in the request
470+ res. contracts_proof . contract_leaves_data [ 0 ] . storage_root ,
471+ res. contracts_proof . contract_leaves_data [ 1 ] . storage_root ,
472+ res. contracts_proof . contract_leaves_data [ 2 ] . storage_root ,
473+ )
474+ } )
475+ . unwrap ( ) ;
476+
477+ assert_eq ! ( actual_contract1_root, expected_contract1_root) ;
478+ assert_eq ! ( actual_contract2_root, expected_contract2_root) ;
479+ assert_eq ! ( actual_contract3_root, expected_contract3_root) ;
480+
481+ //////////////////////////////////////////////////////////////////////////////
482+ // historical state
483+ //////////////////////////////////////////////////////////////////////////////
484+
485+ // this will return the latest state exactly at the forked block
486+ let historical_block = latest_block_number - 5 ;
487+ let state = provider. historical ( historical_block. into ( ) ) . unwrap ( ) . unwrap ( ) ;
488+
489+ //--------------------------------------------------------
490+ // classes root
491+
492+ let actual_classes_root = state. classes_root ( ) . unwrap ( ) ;
493+ let expected_classes_root = starknet_client
494+ . get_storage_proof ( historical_block. into ( ) , None , None , None )
495+ . await
496+ . map ( |res| res. global_roots . classes_tree_root )
497+ . unwrap ( ) ;
498+
499+ assert_eq ! ( actual_classes_root, expected_classes_root) ;
500+
501+ //--------------------------------------------------------
502+ // contracts root
503+
504+ let actual_contracts_root = state. contracts_root ( ) . unwrap ( ) ;
505+ let expected_contracts_root = starknet_client
506+ . get_storage_proof ( historical_block. into ( ) , None , None , None )
507+ . await
508+ . map ( |res| res. global_roots . contracts_tree_root )
509+ . unwrap ( ) ;
510+
511+ assert_eq ! ( actual_contracts_root, expected_contracts_root) ;
512+
513+ //--------------------------------------------------------
514+ // contract storage root
515+
516+ let contract1 = address ! ( "0x049D36570D4e46f48e99674bd3fcc84644DdD6b96F7C741B1562B82f9e004dC7" ) ; // Ether Token
517+ let contract2 = address ! ( "0x04718f5a0Fc34cC1AF16A1cdee98fFB20C31f5cD61D6Ab07201858f4287c938D" ) ; // Starknet Token
518+ let contract3 = address ! ( "0x053C91253BC9682c04929cA02ED00b3E423f6710D2ee7e0D5EBB06F3eCF368A8" ) ; // USDC Token
519+
520+ let actual_contract1_root = state. storage_root ( contract1) . unwrap ( ) . unwrap ( ) ;
521+ let actual_contract2_root = state. storage_root ( contract2) . unwrap ( ) . unwrap ( ) ;
522+ let actual_contract3_root = state. storage_root ( contract3) . unwrap ( ) . unwrap ( ) ;
523+
524+ let ( expected_contract1_root, expected_contract2_root, expected_contract3_root) =
525+ starknet_client
526+ . get_storage_proof (
527+ historical_block. into ( ) ,
528+ None ,
529+ Some ( vec ! [ contract1, contract2, contract3] ) ,
530+ None ,
531+ )
532+ . await
533+ . map ( |res| {
534+ (
535+ // the leave must be ordered based on the order of the contracts in the request
536+ res. contracts_proof . contract_leaves_data [ 0 ] . storage_root ,
537+ res. contracts_proof . contract_leaves_data [ 1 ] . storage_root ,
538+ res. contracts_proof . contract_leaves_data [ 2 ] . storage_root ,
539+ )
540+ } )
541+ . unwrap ( ) ;
542+
543+ assert_eq ! ( actual_contract1_root, expected_contract1_root) ;
544+ assert_eq ! ( actual_contract2_root, expected_contract2_root) ;
545+ assert_eq ! ( actual_contract3_root, expected_contract3_root) ;
546+ }
0 commit comments