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
    frame_support::traits::{fungible::Mutate, tokens::Preservation, Get},
19
    parity_scale_codec::DecodeAll,
20
    snowbridge_core::Channel,
21
    snowbridge_router_primitives::inbound::{
22
        envelope::Envelope, Command, Destination, MessageProcessor, MessageV1, VersionedXcmMessage,
23
    },
24
    sp_runtime::{traits::MaybeEquivalence, DispatchError, DispatchResult},
25
};
26

            
27
/// `NativeTokenTransferMessageProcessor` is responsible for receiving and processing the Tanssi
28
/// native token sent from Ethereum. If the message is valid, it performs the token transfer
29
/// from the Ethereum sovereign account to the specified destination account.
30
pub struct NativeTokenTransferMessageProcessor<T>(sp_std::marker::PhantomData<T>);
31
impl<T> MessageProcessor for NativeTokenTransferMessageProcessor<T>
32
where
33
    T: snowbridge_pallet_inbound_queue::Config
34
        + pallet_ethereum_token_transfers::Config
35
        + snowbridge_pallet_system::Config,
36
    T::AccountId: From<[u8; 32]>,
37
{
38
20
    fn can_process_message(channel: &Channel, envelope: &Envelope) -> bool {
39
        // Ensure that the message is intended for the current channel, para_id and agent_id
40
20
        if let Some(channel_info) = pallet_ethereum_token_transfers::CurrentChannelInfo::<T>::get()
41
        {
42
18
            if envelope.channel_id != channel_info.channel_id
43
16
                || channel.para_id != channel_info.para_id
44
14
                || channel.agent_id != channel_info.agent_id
45
            {
46
6
                log::debug!(
47
                    "Unexpected channel id: {:?} != {:?}",
48
                    (envelope.channel_id, channel.para_id, channel.agent_id),
49
                    (
50
                        channel_info.channel_id,
51
                        channel_info.para_id,
52
                        channel_info.agent_id
53
                    )
54
                );
55
6
                return false;
56
12
            }
57
        } else {
58
2
            log::warn!("CurrentChannelInfo not set in storage");
59
2
            return false;
60
        }
61

            
62
        // Check it is from the right gateway
63
12
        if envelope.gateway != T::GatewayAddress::get() {
64
2
            log::warn!("Wrong gateway address: {:?}", envelope.gateway);
65
2
            return false;
66
10
        }
67
10

            
68
10
        // Try decode the message and check the token id is the expected one
69
10
        match VersionedXcmMessage::decode_all(&mut envelope.payload.as_slice()) {
70
            Ok(VersionedXcmMessage::V1(MessageV1 {
71
8
                command: Command::SendNativeToken { token_id, .. },
72
8
                ..
73
8
            })) => {
74
8
                let token_location = T::TokenLocationReanchored::get();
75

            
76
8
                if let Some(expected_token_id) =
77
8
                    snowbridge_pallet_system::Pallet::<T>::convert_back(&token_location)
78
                {
79
8
                    if token_id == expected_token_id {
80
8
                        return true;
81
                    } else {
82
                        // TODO: ensure this does not warn on container token transfers or other message types, if yes change to debug
83
                        log::warn!(
84
                            "NativeTokenTransferMessageProcessor: unexpected token_id: {:?}",
85
                            token_id
86
                        );
87
                        return false;
88
                    }
89
                } else {
90
                    log::warn!("NativeTokenTransferMessageProcessor: token id not found for location: {:?}", token_location);
91

            
92
                    return false;
93
                }
94
            }
95
2
            Ok(msg) => {
96
2
                log::trace!(
97
                    "NativeTokenTransferMessageProcessor: unexpected message: {:?}",
98
                    msg
99
                );
100
2
                false
101
            }
102
            Err(e) => {
103
                log::trace!("NativeTokenTransferMessageProcessor: failed to decode message. This is expected if the message is not for this processor. Error: {:?}", e);
104
                false
105
            }
106
        }
107
20
    }
108

            
109
12
    fn process_message(_channel: Channel, envelope: Envelope) -> DispatchResult {
110
        // - Decode payload as SendNativeToken
111
12
        let message = VersionedXcmMessage::decode_all(&mut envelope.payload.as_slice())
112
12
        .map_err(|e| {
113
            log::trace!("NativeTokenTransferMessageProcessor: failed to decode message. This is expected if the message is not for this processor. Error: {:?}", e);
114

            
115
            DispatchError::Other("unable to parse the envelope payload")
116
12
        })?;
117

            
118
12
        log::trace!("NativeTokenTransferMessageProcessor: {:?}", message);
119

            
120
12
        match message {
121
            VersionedXcmMessage::V1(MessageV1 {
122
                chain_id: _,
123
                command:
124
                    Command::SendNativeToken {
125
                        destination:
126
                            Destination::AccountId32 {
127
8
                                id: destination_account,
128
8
                            },
129
8
                        amount,
130
8
                        ..
131
8
                    },
132
8
            }) => {
133
8
                // - Transfer the amounts of tokens from Ethereum sov account to the destination
134
8
                let sovereign_account = T::EthereumSovereignAccount::get();
135

            
136
8
                if let Err(e) = T::Currency::transfer(
137
8
                    &sovereign_account,
138
8
                    &destination_account.into(),
139
8
                    amount.into(),
140
8
                    Preservation::Preserve,
141
8
                ) {
142
2
                    log::warn!(
143
                        "NativeTokenTransferMessageProcessor: Error transferring tokens: {:?}",
144
                        e
145
                    );
146
6
                }
147

            
148
8
                Ok(())
149
            }
150
4
            msg => {
151
4
                log::warn!(
152
                    "NativeTokenTransferMessageProcessor: unexpected message: {:?}",
153
                    msg
154
                );
155
4
                Ok(())
156
            }
157
        }
158
12
    }
159
}