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