xrpl_address_macro/
lib.rs1use proc_macro::TokenStream;
22use quote::quote;
23use syn::{LitStr, parse_macro_input};
24
25#[proc_macro]
26pub fn r_address(input: TokenStream) -> TokenStream {
27 let addr_lit = parse_macro_input!(input as LitStr);
28 let addr = addr_lit.value();
29
30 match decode_classic_address_to_20bytes(&addr) {
31 Some(bytes) => {
32 if bytes.len() != 20 {
33 return syn::Error::new(
34 addr_lit.span(),
35 format!("Address decoded to {} bytes, expected 20", bytes.len()),
36 )
37 .to_compile_error()
38 .into();
39 }
40
41 let bytes_tokens = bytes.iter().map(|b| quote! { #b });
42 let expanded = quote! {
43 [#(#bytes_tokens),*]
44 };
45
46 TokenStream::from(expanded)
47 }
48 None => syn::Error::new(addr_lit.span(), format!("Invalid r-address: {addr}"))
49 .to_compile_error()
50 .into(),
51 }
52}
53
54fn decode_classic_address_to_20bytes(addr: &str) -> Option<Vec<u8>> {
55 if !addr.starts_with('r') {
56 return None;
57 }
58 let alphabet =
59 bs58::Alphabet::new(b"rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz").ok()?;
60 let full = bs58::decode(addr)
61 .with_alphabet(&alphabet)
62 .into_vec()
63 .ok()?;
64 if full.len() < 1 + 20 + 4 {
65 return None;
66 }
67 if full[0] != 0x00 {
69 return None;
70 }
71 let (payload, checksum) = full.split_at(full.len() - 4);
73 use sha2::{Digest, Sha256};
75 let first = Sha256::digest(payload);
76 let second = Sha256::digest(first);
77 if &second[0..4] != checksum {
78 return None;
79 }
80 if payload.len() != 1 + 20 {
82 return None;
83 }
84 Some(payload[1..].to_vec())
85}