1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
use std::iter::zip;

use fuels_types::{
    errors::{error, Error, Result},
    param_types::ParamType,
    Token,
};

use crate::abi_decoder::ABIDecoder;

pub trait DecodableLog {
    fn decode_log(&self, data: &[u8]) -> Result<String>;
}

impl DecodableLog for ParamType {
    fn decode_log(&self, data: &[u8]) -> Result<String> {
        let token = ABIDecoder::decode_single(self, data)?;
        paramtype_decode_log(self, &token)
    }
}

fn inner_types_log(tokens: &[Token], inner_type: &ParamType, join_str: &str) -> Result<String> {
    let inner_types_log = tokens
        .iter()
        .map(|token| paramtype_decode_log(inner_type, token))
        .collect::<Result<Vec<_>>>()?
        .join(join_str);

    Ok(inner_types_log)
}

fn paramtype_decode_log(param_type: &ParamType, token: &Token) -> Result<String> {
    let result = match (param_type, token) {
        (ParamType::U8, Token::U8(val)) => val.to_string(),
        (ParamType::U16, Token::U16(val)) => val.to_string(),
        (ParamType::U32, Token::U32(val)) => val.to_string(),
        (ParamType::U64, Token::U64(val)) => val.to_string(),
        (ParamType::Bool, Token::Bool(val)) => val.to_string(),
        (ParamType::B256, Token::B256(val)) => {
            format!("Bits256({val:?})")
        }
        (ParamType::Unit, Token::Unit) => "()".to_string(),
        (ParamType::String(..), Token::String(str_token)) => {
            format!(
                "SizedAsciiString {{ data: \"{}\" }}",
                str_token.get_encodable_str()?
            )
        }
        (ParamType::Array(inner_type, _), Token::Array(tokens)) => {
            let elements = inner_types_log(tokens, inner_type, ", ")?;
            format!("[{elements}]")
        }
        (ParamType::Vector(inner_type), Token::Vector(tokens)) => {
            let elements = inner_types_log(tokens, inner_type, ", ")?;
            format!("[{elements}]")
        }
        (ParamType::Struct { name, fields, .. }, Token::Struct(field_tokens)) => {
            let fields = zip(fields, field_tokens)
                .map(|((field_name, param_type), token)| -> Result<_> {
                    let field_stringified = paramtype_decode_log(param_type, token)?;
                    Ok(format!("{field_name}: {field_stringified}"))
                })
                .collect::<Result<Vec<_>>>()?
                .join(", ");
            format!("{name} {{ {fields} }}")
        }
        (ParamType::Enum { .. }, Token::Enum(selector)) => {
            let (discriminant, token, variants) = selector.as_ref();

            let (variant_name, variant_param_type) = variants.select_variant(*discriminant)?;
            let variant_str = paramtype_decode_log(variant_param_type, token)?;
            let variant_str = if variant_str == "()" {
                "".into()
            } else {
                format!("({variant_str})")
            };

            format!("{variant_name}{variant_str}")
        }
        (ParamType::Tuple(types), Token::Tuple(tokens)) => {
            let elements = zip(types, tokens)
                .map(|(ptype, token)| paramtype_decode_log(ptype, token))
                .collect::<Result<Vec<_>>>()?
                .join(", ");

            format!("({elements})")
        }
        _ => {
            return Err(error!(
                InvalidData,
                "Could not decode log with param type: `{param_type:?}` and token: `{token:?}`"
            ))
        }
    };
    Ok(result)
}

#[cfg(test)]
mod tests {
    use fuels_types::{errors::Result, Bits256, EvmAddress, SizedAsciiString};

    use crate::{traits::DecodableLog, Parameterize};

    #[test]
    fn test_param_type_decode_log() -> Result<()> {
        {
            assert_eq!(
                format!("{:?}", true),
                bool::param_type().decode_log(&[0, 0, 0, 0, 0, 0, 0, 1])?
            );

            assert_eq!(
                format!("{:?}", 128u8),
                u8::param_type().decode_log(&[0, 0, 0, 0, 0, 0, 0, 128])?
            );

            assert_eq!(
                format!("{:?}", 256u16),
                u16::param_type().decode_log(&[0, 0, 0, 0, 0, 0, 1, 0])?
            );

            assert_eq!(
                format!("{:?}", 512u32),
                u32::param_type().decode_log(&[0, 0, 0, 0, 0, 0, 2, 0])?
            );

            assert_eq!(
                format!("{:?}", 1024u64),
                u64::param_type().decode_log(&[0, 0, 0, 0, 0, 0, 4, 0])?
            );
        }
        {
            assert_eq!(
                format!("{:?}", (1, 2)),
                <(u8, u8)>::param_type()
                    .decode_log(&[0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2])?
            );

            assert_eq!(
                format!("{:?}", [3, 4]),
                <[u64; 2]>::param_type()
                    .decode_log(&[0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 4])?
            );

            assert_eq!(
                format!("{:?}", SizedAsciiString::<4>::new("Fuel".to_string())?),
                SizedAsciiString::<4>::param_type().decode_log(&[70, 117, 101, 108, 0, 0, 0, 0])?
            );
        }
        {
            assert_eq!(
                format!("{:?}", Some(42)),
                <Option<u64>>::param_type()
                    .decode_log(&[0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 42])?
            );

            assert_eq!(
                format!("{:?}", Err::<u64, u64>(42u64)),
                <std::result::Result<u64, u64>>::param_type()
                    .decode_log(&[0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 42])?
            );

            let bits256 = Bits256([
                239, 134, 175, 169, 105, 108, 240, 220, 99, 133, 226, 196, 7, 166, 225, 89, 161,
                16, 60, 239, 183, 226, 174, 6, 54, 251, 51, 211, 203, 42, 158, 74,
            ]);

            assert_eq!(
                format!("{bits256:?}"),
                Bits256::param_type().decode_log(&[
                    239, 134, 175, 169, 105, 108, 240, 220, 99, 133, 226, 196, 7, 166, 225, 89,
                    161, 16, 60, 239, 183, 226, 174, 6, 54, 251, 51, 211, 203, 42, 158, 74
                ])?
            );

            assert_eq!(
                format!("{:?}", EvmAddress::from(bits256)),
                EvmAddress::param_type().decode_log(&[
                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 166, 225, 89, 161, 16, 60, 239, 183,
                    226, 174, 6, 54, 251, 51, 211, 203, 42, 158, 74
                ])?
            );
        }

        Ok(())
    }
}