1use std::marker::PhantomData;
5
6use borsh::{BorshDeserialize, BorshSchema, BorshSerialize};
7#[doc(inline)]
8pub use ethabi::token::Token;
9
10use crate::keccak::{KeccakHash, keccak_hash};
11use crate::key::{Signable, SignableEthMessage};
12
13#[derive(Clone, Debug, BorshSerialize, BorshDeserialize, BorshSchema)]
15#[repr(transparent)]
16pub struct EncodeCell<T: ?Sized> {
17 encoded_data: Vec<u8>,
19 _marker: PhantomData<*const T>,
25}
26
27impl<T> AsRef<[u8]> for EncodeCell<T> {
28 fn as_ref(&self) -> &[u8] {
29 &self.encoded_data
30 }
31}
32
33impl<T> ::std::cmp::Eq for EncodeCell<T> {}
34
35impl<T> ::std::cmp::PartialEq for EncodeCell<T> {
36 fn eq(&self, other: &Self) -> bool {
37 self.encoded_data == other.encoded_data
38 }
39}
40
41impl<T> ::std::cmp::PartialOrd for EncodeCell<T> {
42 fn partial_cmp(&self, other: &Self) -> Option<::std::cmp::Ordering> {
43 Some(self.cmp(other))
44 }
45}
46
47impl<T> ::std::cmp::Ord for EncodeCell<T> {
48 fn cmp(&self, other: &Self) -> ::std::cmp::Ordering {
49 self.encoded_data.cmp(&other.encoded_data)
50 }
51}
52
53impl<T> EncodeCell<T> {
54 pub fn new<const N: usize>(value: &T) -> Self
56 where
57 T: Encode<N>,
58 {
59 let encoded_data = {
60 let tokens = value.tokenize();
61 ethabi::encode(tokens.as_slice())
62 };
63 Self {
64 encoded_data,
65 _marker: PhantomData,
66 }
67 }
68
69 pub fn new_from<const N: usize>(tokens: [Token; N]) -> Self {
72 Self {
73 encoded_data: ethabi::encode(&tokens),
74 _marker: PhantomData,
75 }
76 }
77
78 pub fn into_inner(self) -> Vec<u8> {
80 self.encoded_data
81 }
82}
83
84pub trait Encode<const N: usize>: Sized {
86 fn tokenize(&self) -> [Token; N];
89
90 fn encode(&self) -> EncodeCell<Self> {
92 EncodeCell::new(self)
93 }
94
95 fn keccak256(&self) -> KeccakHash {
98 keccak_hash(self.encode().into_inner().as_slice())
99 }
100
101 fn signable_keccak256(&self) -> KeccakHash {
105 let message = self.keccak256();
106 SignableEthMessage::as_signable(&message)
107 }
108}
109
110pub type AbiEncode<const N: usize> = [Token; N];
113
114impl<const N: usize> Encode<N> for AbiEncode<N> {
115 #[inline]
116 fn tokenize(&self) -> [Token; N] {
117 self.clone()
118 }
119}
120
121#[cfg(test)]
122mod tests {
123 use std::str::FromStr;
124
125 use data_encoding::HEXLOWER;
126 use ethabi::ethereum_types::U256;
127 use tiny_keccak::{Hasher, Keccak};
128
129 use super::*;
130 use crate::ethereum_events::EthAddress;
131
132 #[test]
135 fn test_abi_encode() {
136 let expected = "0x000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000047465737400000000000000000000000000000000000000000000000000000000";
137 let expected = HEXLOWER
138 .decode(&expected.as_bytes()[2..])
139 .expect("Test failed");
140 let got = AbiEncode::encode(&[
141 Token::Uint(U256::from(42u64)),
142 Token::String("test".into()),
143 ]);
144 assert_eq!(expected, got.into_inner());
145 }
146
147 #[test]
149 fn test_keccak_hash_impl() {
150 let expected =
151 "1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8";
152 assert_eq!(
153 expected,
154 &HEXLOWER.encode(
155 &{
156 let mut st = Keccak::v256();
157 let mut output = [0; 32];
158 st.update(b"hello");
159 st.finalize(&mut output);
160 output
161 }[..]
162 )
163 );
164 }
165
166 #[test]
169 fn test_hex_roundtrip() {
170 let original =
171 "1C8AFF950685C2ED4BC3174F3472287B56D9517B9C948127319A09A7A36DEAC8";
172 let keccak_hash: KeccakHash = original.try_into().expect("Test failed");
173 assert_eq!(keccak_hash.to_string().as_str(), original);
174 }
175
176 #[test]
177 fn test_abi_encode_address() {
178 let address =
179 EthAddress::from_str("0xF0457e703bf0B9dEb1a6003FFD71C77E44575f95")
180 .expect("Test failed");
181 let expected = "0x000000000000000000000000f0457e703bf0b9deb1a6003ffd71c77e44575f95";
182 let expected = HEXLOWER
183 .decode(&expected.as_bytes()[2..])
184 .expect("Test failed");
185 let encoded = ethabi::encode(&[Token::Address(address.0.into())]);
186 assert_eq!(expected, encoded);
187 }
188}