@@ -27,7 +27,7 @@ use snowbridge_pallet_inbound_queue_fixtures::{
2727} ;
2828use snowbridge_pallet_system;
2929use snowbridge_router_primitives:: inbound:: {
30- Command , GlobalConsensusEthereumConvertsFor , MessageV1 , VersionedMessage ,
30+ Command , Destination , GlobalConsensusEthereumConvertsFor , MessageV1 , VersionedMessage ,
3131} ;
3232use sp_core:: H256 ;
3333use sp_runtime:: { DispatchError :: Token , TokenError :: FundsUnavailable } ;
@@ -40,6 +40,7 @@ const TREASURY_ACCOUNT: [u8; 32] =
4040const WETH : [ u8 ; 20 ] = hex ! ( "87d1f7fdfEe7f651FaBc8bFCB6E086C278b77A7d" ) ;
4141const ETHEREUM_DESTINATION_ADDRESS : [ u8 ; 20 ] = hex ! ( "44a57ee2f2FCcb85FDa2B0B18EBD0D8D2333700e" ) ;
4242const INSUFFICIENT_XCM_FEE : u128 = 1000 ;
43+ const XCM_FEE : u128 = 4_000_000_000 ;
4344
4445#[ derive( Encode , Decode , Debug , PartialEq , Eq , Clone , TypeInfo ) ]
4546pub enum ControlCall {
@@ -555,3 +556,132 @@ fn register_weth_token_in_asset_hub_fail_for_insufficient_fee() {
555556 ) ;
556557 } ) ;
557558}
559+
560+ fn send_token_from_ethereum_to_asset_hub_with_fee ( account_id : [ u8 ; 32 ] , fee : u128 ) {
561+ let weth_asset_location: Location = Location :: new (
562+ 2 ,
563+ [ EthereumNetwork :: get ( ) . into ( ) , AccountKey20 { network : None , key : WETH } ] ,
564+ ) ;
565+ // (Parent, Parent, EthereumNetwork::get(), AccountKey20 { network: None, key: WETH })
566+ // Fund asset hub sovereign on bridge hub
567+ let asset_hub_sovereign = BridgeHubRococo :: sovereign_account_id_of ( Location :: new (
568+ 1 ,
569+ [ Parachain ( AssetHubRococo :: para_id ( ) . into ( ) ) ] ,
570+ ) ) ;
571+ BridgeHubRococo :: fund_accounts ( vec ! [ ( asset_hub_sovereign. clone( ) , INITIAL_FUND ) ] ) ;
572+
573+ // Register WETH
574+ AssetHubRococo :: execute_with ( || {
575+ type RuntimeOrigin = <AssetHubRococo as Chain >:: RuntimeOrigin ;
576+
577+ assert_ok ! ( <AssetHubRococo as AssetHubRococoPallet >:: ForeignAssets :: force_create(
578+ RuntimeOrigin :: root( ) ,
579+ weth_asset_location. clone( ) . try_into( ) . unwrap( ) ,
580+ asset_hub_sovereign. into( ) ,
581+ false ,
582+ 1 ,
583+ ) ) ;
584+
585+ assert ! ( <AssetHubRococo as AssetHubRococoPallet >:: ForeignAssets :: asset_exists(
586+ weth_asset_location. clone( ) . try_into( ) . unwrap( ) ,
587+ ) ) ;
588+ } ) ;
589+
590+ // Send WETH to an existent account on asset hub
591+ BridgeHubRococo :: execute_with ( || {
592+ type RuntimeEvent = <BridgeHubRococo as Chain >:: RuntimeEvent ;
593+
594+ type EthereumInboundQueue =
595+ <BridgeHubRococo as BridgeHubRococoPallet >:: EthereumInboundQueue ;
596+ let message_id: H256 = [ 0 ; 32 ] . into ( ) ;
597+ let message = VersionedMessage :: V1 ( MessageV1 {
598+ chain_id : CHAIN_ID ,
599+ command : Command :: SendToken {
600+ token : WETH . into ( ) ,
601+ destination : Destination :: AccountId32 { id : account_id } ,
602+ amount : 1_000_000 ,
603+ fee,
604+ } ,
605+ } ) ;
606+ let ( xcm, _) = EthereumInboundQueue :: do_convert ( message_id, message) . unwrap ( ) ;
607+ assert_ok ! ( EthereumInboundQueue :: send_xcm( xcm, AssetHubRococo :: para_id( ) . into( ) ) ) ;
608+
609+ // Check that the message was sent
610+ assert_expected_events ! (
611+ BridgeHubRococo ,
612+ vec![
613+ RuntimeEvent :: XcmpQueue ( cumulus_pallet_xcmp_queue:: Event :: XcmpMessageSent { .. } ) => { } ,
614+ ]
615+ ) ;
616+ } ) ;
617+ }
618+
619+ #[ test]
620+ fn send_token_from_ethereum_to_existent_account_on_asset_hub ( ) {
621+ send_token_from_ethereum_to_asset_hub_with_fee ( AssetHubRococoSender :: get ( ) . into ( ) , XCM_FEE ) ;
622+
623+ AssetHubRococo :: execute_with ( || {
624+ type RuntimeEvent = <AssetHubRococo as Chain >:: RuntimeEvent ;
625+
626+ // Check that the token was received and issued as a foreign asset on AssetHub
627+ assert_expected_events ! (
628+ AssetHubRococo ,
629+ vec![
630+ RuntimeEvent :: ForeignAssets ( pallet_assets:: Event :: Issued { .. } ) => { } ,
631+ ]
632+ ) ;
633+ } ) ;
634+ }
635+
636+ #[ test]
637+ fn send_token_from_ethereum_to_non_existent_account_on_asset_hub ( ) {
638+ send_token_from_ethereum_to_asset_hub_with_fee ( [ 1 ; 32 ] , XCM_FEE ) ;
639+
640+ AssetHubRococo :: execute_with ( || {
641+ type RuntimeEvent = <AssetHubRococo as Chain >:: RuntimeEvent ;
642+
643+ // Check that the token was received and issued as a foreign asset on AssetHub
644+ assert_expected_events ! (
645+ AssetHubRococo ,
646+ vec![
647+ RuntimeEvent :: ForeignAssets ( pallet_assets:: Event :: Issued { .. } ) => { } ,
648+ ]
649+ ) ;
650+ } ) ;
651+ }
652+
653+ #[ test]
654+ fn send_token_from_ethereum_to_non_existent_account_on_asset_hub_with_insufficient_fee ( ) {
655+ send_token_from_ethereum_to_asset_hub_with_fee ( [ 1 ; 32 ] , INSUFFICIENT_XCM_FEE ) ;
656+
657+ AssetHubRococo :: execute_with ( || {
658+ type RuntimeEvent = <AssetHubRococo as Chain >:: RuntimeEvent ;
659+
660+ // Check that the token was received and issued as a foreign asset on AssetHub
661+ assert_expected_events ! (
662+ AssetHubRococo ,
663+ vec![
664+ RuntimeEvent :: MessageQueue ( pallet_message_queue:: Event :: Processed { success: false , .. } ) => { } ,
665+ ]
666+ ) ;
667+ } ) ;
668+ }
669+
670+ #[ test]
671+ fn send_token_from_ethereum_to_non_existent_account_on_asset_hub_with_sufficient_fee_but_do_not_satisfy_ed (
672+ ) {
673+ // On AH the xcm fee is 33_873_024 and the ED is 3_300_000
674+ send_token_from_ethereum_to_asset_hub_with_fee ( [ 1 ; 32 ] , 36_000_000 ) ;
675+
676+ AssetHubRococo :: execute_with ( || {
677+ type RuntimeEvent = <AssetHubRococo as Chain >:: RuntimeEvent ;
678+
679+ // Check that the token was received and issued as a foreign asset on AssetHub
680+ assert_expected_events ! (
681+ AssetHubRococo ,
682+ vec![
683+ RuntimeEvent :: MessageQueue ( pallet_message_queue:: Event :: Processed { success: false , .. } ) => { } ,
684+ ]
685+ ) ;
686+ } ) ;
687+ }
0 commit comments