1
// Copyright (C) Moondance Labs Ltd.
2
// This file is part of Tanssi.
3

            
4
// Tanssi is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8

            
9
// Tanssi is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13

            
14
// You should have received a copy of the GNU General Public License
15
// along with Tanssi.  If not, see <http://www.gnu.org/licenses/>
16

            
17
use {
18
    alloc::vec,
19
    core::marker::PhantomData,
20
    frame_support::traits::Get,
21
    xcm::{
22
        latest::{Location, NetworkId},
23
        prelude::*,
24
    },
25
    xcm_builder::InspectMessageQueues,
26
    SendError::*,
27
};
28

            
29
// We check if the destination is ETH
30
24
fn is_ethereum_location(loc: &Location, ethereum_network: NetworkId) -> bool {
31
24
    matches!(
32
24
        loc,
33
24
        Location {
34
24
            parents: 2,
35
24
            interior: Junctions::X1(juncs)
36
24
        } if juncs[0] == GlobalConsensus(ethereum_network)
37
    )
38
24
}
39

            
40
pub struct SovereignPaidRemoteExporter<Router, UniversalLocation, EthereumNetwork, ExecutionFee>(
41
    PhantomData<(Router, UniversalLocation, EthereumNetwork, ExecutionFee)>,
42
);
43
impl<Router: SendXcm, UniversalLocation: Get<InteriorLocation>, EthereumNetwork, ExecutionFee>
44
    SendXcm
45
    for SovereignPaidRemoteExporter<Router, UniversalLocation, EthereumNetwork, ExecutionFee>
46
where
47
    EthereumNetwork: Get<NetworkId>,
48
    ExecutionFee: Get<u128>,
49
{
50
    type Ticket = Router::Ticket;
51

            
52
2
    fn validate(
53
2
        dest: &mut Option<Location>,
54
2
        msg: &mut Option<Xcm<()>>,
55
2
    ) -> SendResult<Router::Ticket> {
56
2
        let d = dest.as_ref().ok_or(MissingArgument)?;
57
2
        let xcm = msg.take().ok_or(MissingArgument)?;
58
        // Check if the destination is an Ethereum location
59
2
        if !is_ethereum_location(&d.clone(), EthereumNetwork::get()) {
60
            return Err(NotApplicable);
61
2
        }
62

            
63
        // `xcm` should already end with `SetTopic` - if it does, then extract and derive into
64
        // an onward topic ID.
65
2
        let maybe_forward_id = match xcm.last() {
66
2
            Some(SetTopic(t)) => Some(*t),
67
            _ => None,
68
        };
69

            
70
2
        let export_instruction = ExportMessage {
71
2
            network: EthereumNetwork::get(),
72
2
            destination: Here,
73
2
            xcm: xcm.clone(),
74
2
        };
75
2

            
76
2
        // For now hardcoding fees, but later it should be converted from fixed Tanssi relay amount
77
2
        let fees = Asset {
78
2
            id: AssetId(Location::here()),
79
2
            fun: Fungible(ExecutionFee::get()),
80
2
        };
81
2
        let network = EthereumNetwork::get();
82
2
        let eth_location = Location::new(2, GlobalConsensus(network));
83
2

            
84
2
        // Prepare the message to send
85
2
        let mut message = Xcm(vec![
86
2
            WithdrawAsset(fees.clone().into()),
87
2
            BuyExecution {
88
2
                fees,
89
2
                weight_limit: Unlimited,
90
2
            },
91
2
            SetAppendix(Xcm(vec![DepositAsset {
92
2
                assets: AllCounted(1).into(),
93
2
                beneficiary: eth_location,
94
2
            }])),
95
2
            export_instruction,
96
2
        ]);
97
2

            
98
2
        let tanssi_location = Location {
99
2
            parents: 1,
100
2
            interior: Here,
101
2
        };
102

            
103
2
        if let Some(forward_id) = maybe_forward_id {
104
2
            message.0.push(SetTopic(forward_id));
105
2
        }
106

            
107
2
        let (v, cost) = validate_send::<Router>(tanssi_location, message).inspect_err(|err| {
108
            if let NotApplicable = err {
109
                *msg = Some(xcm);
110
            }
111
2
        })?;
112

            
113
2
        Ok((v, cost))
114
2
    }
115

            
116
2
    fn deliver(ticket: Router::Ticket) -> Result<XcmHash, SendError> {
117
2
        Router::deliver(ticket)
118
2
    }
119
}
120

            
121
impl<Router: SendXcm, UniversalLocation: Get<InteriorLocation>, EthereumNetwork, ExecutionFee>
122
    InspectMessageQueues
123
    for SovereignPaidRemoteExporter<Router, UniversalLocation, EthereumNetwork, ExecutionFee>
124
{
125
    fn clear_messages() {}
126

            
127
    fn get_messages() -> vec::Vec<(VersionedLocation, vec::Vec<VersionedXcm<()>>)> {
128
        vec::Vec::new()
129
    }
130
}