1use num_bigint::{BigInt, Sign};
30use serde_json::{json, Map, Value};
31
32use crate::decode::ValueExt;
33use crate::error::{Error, Result};
34use crate::rpc::Handler;
35
36#[derive(Debug, Clone, PartialEq)]
39pub enum Token {
40 Address([u8; 20]),
42 Uint(BigInt),
44 Int(BigInt),
46 Bool(bool),
48 FixedBytes(Vec<u8>),
50 Bytes(Vec<u8>),
52 String(String),
54 Array(Vec<Token>),
56}
57
58impl Token {
59 pub fn address(s: &str) -> Result<Token> {
62 let bytes = from_hex(strip_0x(s))?;
63 if bytes.len() != 20 {
64 return Err(Error::Other(format!(
65 "address must be 20 bytes, got {}",
66 bytes.len()
67 )));
68 }
69 let mut a = [0u8; 20];
70 a.copy_from_slice(&bytes);
71 Ok(Token::Address(a))
72 }
73
74 pub fn uint(v: impl Into<BigInt>) -> Token {
76 Token::Uint(v.into())
77 }
78
79 pub fn as_uint(&self) -> Option<&BigInt> {
81 match self {
82 Token::Uint(v) | Token::Int(v) => Some(v),
83 _ => None,
84 }
85 }
86
87 pub fn as_address(&self) -> Option<[u8; 20]> {
89 match self {
90 Token::Address(a) => Some(*a),
91 _ => None,
92 }
93 }
94
95 pub fn as_address_hex(&self) -> Option<String> {
97 self.as_address().map(|a| format!("0x{}", to_hex(&a)))
98 }
99
100 pub fn as_bool(&self) -> Option<bool> {
102 match self {
103 Token::Bool(b) => Some(*b),
104 _ => None,
105 }
106 }
107
108 pub fn as_bytes(&self) -> Option<&[u8]> {
110 match self {
111 Token::Bytes(b) | Token::FixedBytes(b) => Some(b),
112 _ => None,
113 }
114 }
115
116 pub fn as_string(&self) -> Option<&str> {
118 match self {
119 Token::String(s) => Some(s),
120 _ => None,
121 }
122 }
123
124 pub fn as_array(&self) -> Option<&[Token]> {
126 match self {
127 Token::Array(v) => Some(v),
128 _ => None,
129 }
130 }
131
132 fn is_dynamic(&self) -> bool {
133 matches!(self, Token::Bytes(_) | Token::String(_) | Token::Array(_))
134 }
135}
136
137#[derive(Debug, Clone, PartialEq)]
140pub enum ParamType {
141 Address,
143 Uint(usize),
145 Int(usize),
147 Bool,
149 FixedBytes(usize),
151 Bytes,
153 String,
155 Array(Box<ParamType>),
157}
158
159impl ParamType {
160 fn is_dynamic(&self) -> bool {
161 matches!(
162 self,
163 ParamType::Bytes | ParamType::String | ParamType::Array(_)
164 )
165 }
166}
167
168pub fn function_selector(signature: &str) -> [u8; 4] {
172 let h = purecrypto::hash::keccak256(signature.as_bytes());
173 [h[0], h[1], h[2], h[3]]
174}
175
176pub fn encode_call(signature: &str, args: &[Token]) -> Vec<u8> {
179 let mut out = function_selector(signature).to_vec();
180 out.extend(encode(args));
181 out
182}
183
184pub fn encode(tokens: &[Token]) -> Vec<u8> {
186 let head_len: usize = tokens.len() * 32;
187 let mut head = Vec::with_capacity(head_len);
188 let mut tail = Vec::new();
189 for t in tokens {
190 if t.is_dynamic() {
191 head.extend_from_slice(&word_usize(head_len + tail.len()));
192 tail.extend(encode_value(t));
193 } else {
194 head.extend(encode_value(t));
195 }
196 }
197 head.extend(tail);
198 head
199}
200
201fn encode_value(t: &Token) -> Vec<u8> {
203 match t {
204 Token::Address(a) => {
205 let mut w = [0u8; 32];
206 w[12..].copy_from_slice(a);
207 w.to_vec()
208 }
209 Token::Uint(v) => encode_uint(v),
210 Token::Int(v) => encode_int(v),
211 Token::Bool(b) => {
212 let mut w = [0u8; 32];
213 w[31] = *b as u8;
214 w.to_vec()
215 }
216 Token::FixedBytes(b) => {
217 let mut w = [0u8; 32];
218 w[..b.len()].copy_from_slice(b);
219 w.to_vec()
220 }
221 Token::Bytes(b) => encode_dynamic_bytes(b),
222 Token::String(s) => encode_dynamic_bytes(s.as_bytes()),
223 Token::Array(elems) => {
224 let mut out = word_usize(elems.len()).to_vec();
225 out.extend(encode(elems));
226 out
227 }
228 }
229}
230
231fn encode_dynamic_bytes(b: &[u8]) -> Vec<u8> {
232 let mut out = word_usize(b.len()).to_vec();
233 out.extend_from_slice(b);
234 let padded = b.len().div_ceil(32) * 32;
236 out.resize(32 + padded, 0);
237 out
238}
239
240fn encode_uint(v: &BigInt) -> Vec<u8> {
241 let (sign, mag) = v.to_bytes_be();
242 if sign == Sign::Minus {
243 return encode_int(v);
245 }
246 let mut w = [0u8; 32];
247 let start = 32usize.saturating_sub(mag.len());
248 w[start..].copy_from_slice(&mag[mag.len().saturating_sub(32)..]);
249 w.to_vec()
250}
251
252fn encode_int(v: &BigInt) -> Vec<u8> {
253 let encoded = if v.sign() == Sign::Minus {
254 let modulus = BigInt::from(1) << 256;
256 modulus + v
257 } else {
258 v.clone()
259 };
260 let (_, mag) = encoded.to_bytes_be();
261 let mut w = [0u8; 32];
262 let take = mag.len().min(32);
263 w[32 - take..].copy_from_slice(&mag[mag.len() - take..]);
264 w.to_vec()
265}
266
267pub fn decode(types: &[ParamType], data: &[u8]) -> Result<Vec<Token>> {
269 decode_tuple(types, data, 0)
270}
271
272fn decode_tuple(types: &[ParamType], data: &[u8], base: usize) -> Result<Vec<Token>> {
273 let mut out = Vec::with_capacity(types.len());
274 for (i, ty) in types.iter().enumerate() {
275 let head_pos = base + i * 32;
276 if ty.is_dynamic() {
277 let off = read_usize(data, head_pos)?;
278 out.push(decode_value(ty, data, base + off)?);
279 } else {
280 out.push(decode_static(ty, data, head_pos)?);
281 }
282 }
283 Ok(out)
284}
285
286fn decode_static(ty: &ParamType, data: &[u8], pos: usize) -> Result<Token> {
287 let w = word(data, pos)?;
288 Ok(match ty {
289 ParamType::Address => {
290 let mut a = [0u8; 20];
291 a.copy_from_slice(&w[12..32]);
292 Token::Address(a)
293 }
294 ParamType::Uint(_) => Token::Uint(BigInt::from_bytes_be(Sign::Plus, w)),
295 ParamType::Int(_) => {
296 let mut v = BigInt::from_bytes_be(Sign::Plus, w);
297 if w[0] & 0x80 != 0 {
298 v -= BigInt::from(1) << 256;
299 }
300 Token::Int(v)
301 }
302 ParamType::Bool => Token::Bool(w.iter().any(|&b| b != 0)),
303 ParamType::FixedBytes(n) => Token::FixedBytes(w[..(*n).min(32)].to_vec()),
304 _ => return Err(Error::Other("decode_static called on dynamic type".into())),
305 })
306}
307
308fn decode_value(ty: &ParamType, data: &[u8], pos: usize) -> Result<Token> {
309 match ty {
310 ParamType::Bytes => {
311 let len = read_usize(data, pos)?;
312 let start = pos + 32;
313 let end = start
314 .checked_add(len)
315 .ok_or_else(|| Error::Other("abi length overflow".into()))?;
316 slice(data, start, end).map(|s| Token::Bytes(s.to_vec()))
317 }
318 ParamType::String => {
319 let len = read_usize(data, pos)?;
320 let start = pos + 32;
321 let end = start
322 .checked_add(len)
323 .ok_or_else(|| Error::Other("abi length overflow".into()))?;
324 let s = slice(data, start, end)?;
325 Ok(Token::String(String::from_utf8(s.to_vec()).map_err(
326 |_| Error::Other("invalid utf-8 in string".into()),
327 )?))
328 }
329 ParamType::Array(inner) => {
330 let len = read_usize(data, pos)?;
331 let elem_base = pos + 32;
332 let types = vec![(**inner).clone(); len];
333 Ok(Token::Array(decode_tuple(&types, data, elem_base)?))
334 }
335 _ => decode_static(ty, data, pos),
336 }
337}
338
339pub async fn eth_call_abi<H: Handler + ?Sized>(
343 handler: &H,
344 to: &str,
345 signature: &str,
346 args: &[Token],
347 returns: &[ParamType],
348) -> Result<Vec<Token>> {
349 let calldata = encode_call(signature, args);
350 let mut tx = Map::new();
351 tx.insert("to".to_string(), json!(to));
352 tx.insert(
353 "data".to_string(),
354 json!(format!("0x{}", to_hex(&calldata))),
355 );
356
357 let ret = handler
358 .call("eth_call", vec![Value::Object(tx), json!("latest")])
359 .await?;
360 let hex = ret.to_str()?;
361 let bytes = from_hex(strip_0x(hex))?;
362 decode(returns, &bytes)
363}
364
365fn word_usize(v: usize) -> [u8; 32] {
368 let mut w = [0u8; 32];
369 w[24..].copy_from_slice(&(v as u64).to_be_bytes());
370 w
371}
372
373fn word(data: &[u8], pos: usize) -> Result<&[u8]> {
374 slice(data, pos, pos + 32)
375}
376
377fn read_usize(data: &[u8], pos: usize) -> Result<usize> {
378 let w = word(data, pos)?;
379 if w[..24].iter().any(|&b| b != 0) {
381 return Err(Error::Other("abi offset/length too large".into()));
382 }
383 let mut buf = [0u8; 8];
384 buf.copy_from_slice(&w[24..32]);
385 Ok(u64::from_be_bytes(buf) as usize)
386}
387
388fn slice(data: &[u8], start: usize, end: usize) -> Result<&[u8]> {
389 data.get(start..end)
390 .ok_or_else(|| Error::Other("abi data truncated".into()))
391}
392
393fn strip_0x(s: &str) -> &str {
394 s.strip_prefix("0x")
395 .or_else(|| s.strip_prefix("0X"))
396 .unwrap_or(s)
397}
398
399fn to_hex(bytes: &[u8]) -> String {
400 let mut s = String::with_capacity(bytes.len() * 2);
401 for b in bytes {
402 s.push_str(&format!("{b:02x}"));
403 }
404 s
405}
406
407fn from_hex(s: &str) -> Result<Vec<u8>> {
408 if !s.len().is_multiple_of(2) {
409 return Err(Error::Other("odd-length hex string".into()));
410 }
411 (0..s.len())
412 .step_by(2)
413 .map(|i| {
414 u8::from_str_radix(&s[i..i + 2], 16).map_err(|_| Error::Other("invalid hex".into()))
415 })
416 .collect()
417}
418
419#[cfg(test)]
420mod tests {
421 use super::*;
422
423 fn hex(s: &str) -> Vec<u8> {
424 from_hex(strip_0x(s)).unwrap()
425 }
426
427 #[test]
428 fn selectors() {
429 assert_eq!(to_hex(&function_selector("balanceOf(address)")), "70a08231");
430 assert_eq!(
431 to_hex(&function_selector("transfer(address,uint256)")),
432 "a9059cbb"
433 );
434 }
435
436 #[test]
437 fn encode_transfer_calldata() {
438 let to = Token::address("0x0000000000000000000000000000000000000064").unwrap();
440 let calldata = encode_call("transfer(address,uint256)", &[to, Token::uint(1u64)]);
441 let expected = hex("a9059cbb\
442 0000000000000000000000000000000000000000000000000000000000000064\
443 0000000000000000000000000000000000000000000000000000000000000001");
444 assert_eq!(calldata, expected);
445 }
446
447 #[test]
448 fn roundtrip_static() {
449 let tokens = vec![
450 Token::address("0x00000000000000000000000000000000000000ff").unwrap(),
451 Token::uint(12345u64),
452 Token::Bool(true),
453 ];
454 let enc = encode(&tokens);
455 let dec = decode(
456 &[ParamType::Address, ParamType::Uint(256), ParamType::Bool],
457 &enc,
458 )
459 .unwrap();
460 assert_eq!(dec, tokens);
461 }
462
463 #[test]
464 fn roundtrip_dynamic_string_and_bytes() {
465 let tokens = vec![
466 Token::String("hello world, this is longer than 32 bytes!!".to_string()),
467 Token::Uint(BigInt::from(7)),
468 Token::Bytes(vec![1, 2, 3, 4, 5]),
469 ];
470 let enc = encode(&tokens);
471 let dec = decode(
472 &[ParamType::String, ParamType::Uint(256), ParamType::Bytes],
473 &enc,
474 )
475 .unwrap();
476 assert_eq!(dec, tokens);
477 }
478
479 #[test]
480 fn roundtrip_array_of_addresses() {
481 let arr = Token::Array(vec![
482 Token::address("0x0000000000000000000000000000000000000001").unwrap(),
483 Token::address("0x0000000000000000000000000000000000000002").unwrap(),
484 ]);
485 let enc = encode(std::slice::from_ref(&arr));
486 let dec = decode(&[ParamType::Array(Box::new(ParamType::Address))], &enc).unwrap();
487 assert_eq!(dec, vec![arr]);
488 }
489
490 #[test]
491 fn decode_negative_int() {
492 let data = hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
494 let dec = decode(&[ParamType::Int(256)], &data).unwrap();
495 assert_eq!(dec[0].as_uint().unwrap(), &BigInt::from(-1));
496 assert_eq!(encode(&[Token::Int(BigInt::from(-1))]), data);
498 }
499
500 #[test]
501 fn decode_uint_string_result() {
502 let tokens = vec![Token::String("USDC".to_string())];
504 let enc = encode(&tokens);
505 let dec = decode(&[ParamType::String], &enc).unwrap();
506 assert_eq!(dec[0].as_string(), Some("USDC"));
507 }
508}