tap_caip/
lib.rs

1/// CAIP: Chain Agnostic Identifier Standard Implementation
2///
3/// This library provides a Rust implementation of the Chain Agnostic Identifier Standards,
4/// including CAIP-2 (Chain ID), CAIP-10 (Account ID), and CAIP-19 (Asset ID).
5///
6/// See <https://github.com/ChainAgnostic/CAIPs> for the full specifications.
7// Re-export main structs and functions
8mod account_id;
9mod asset_id;
10mod chain_id;
11pub mod error;
12mod validation;
13
14pub use account_id::AccountId;
15pub use asset_id::AssetId;
16pub use chain_id::ChainId;
17pub use error::Error;
18pub use validation::{ValidationRegistry, ValidatorFn};
19
20use std::str::FromStr;
21
22/// Type representing any CAIP identifier
23#[derive(Debug, Clone, PartialEq, Eq)]
24pub enum CaipId {
25    /// CAIP-2 Chain ID
26    ChainId(ChainId),
27    /// CAIP-10 Account ID
28    AccountId(AccountId),
29    /// CAIP-19 Asset ID
30    AssetId(AssetId),
31}
32
33/// Attempt to parse a string as any recognized CAIP identifier
34///
35/// This function will try to parse the string as a ChainId, AccountId, or AssetId
36/// in that order, and return the first successful parse.
37///
38/// # Arguments
39///
40/// * `s` - The string to parse
41///
42/// # Returns
43///
44/// * `Result<CaipId, Error>` - The parsed CAIP identifier or an error
45pub fn parse(s: &str) -> Result<CaipId, error::Error> {
46    if let Ok(chain_id) = ChainId::from_str(s) {
47        return Ok(CaipId::ChainId(chain_id));
48    }
49
50    if let Ok(account_id) = AccountId::from_str(s) {
51        return Ok(CaipId::AccountId(account_id));
52    }
53
54    if let Ok(asset_id) = AssetId::from_str(s) {
55        return Ok(CaipId::AssetId(asset_id));
56    }
57
58    Err(error::Error::UnrecognizedFormat(s.to_string()))
59}
60
61impl std::fmt::Display for CaipId {
62    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63        match self {
64            CaipId::ChainId(id) => write!(f, "{}", id),
65            CaipId::AccountId(id) => write!(f, "{}", id),
66            CaipId::AssetId(id) => write!(f, "{}", id),
67        }
68    }
69}
70
71#[cfg(test)]
72mod tests {
73    use super::*;
74
75    #[test]
76    fn test_parse_chain_id() {
77        let id = parse("eip155:1").unwrap();
78        match id {
79            CaipId::ChainId(chain_id) => {
80                assert_eq!(chain_id.namespace(), "eip155");
81                assert_eq!(chain_id.reference(), "1");
82            }
83            _ => panic!("Expected ChainId"),
84        }
85    }
86
87    #[test]
88    fn test_parse_account_id() {
89        let id = parse("eip155:1:0x4b20993Bc481177ec7E8f571ceCaE8A9e22C02db").unwrap();
90        match id {
91            CaipId::AccountId(account_id) => {
92                assert_eq!(account_id.chain_id().namespace(), "eip155");
93                assert_eq!(account_id.chain_id().reference(), "1");
94                assert_eq!(
95                    account_id.address(),
96                    "0x4b20993Bc481177ec7E8f571ceCaE8A9e22C02db"
97                );
98            }
99            _ => panic!("Expected AccountId"),
100        }
101    }
102
103    #[test]
104    fn test_parse_asset_id() {
105        let id = parse("eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap();
106        match id {
107            CaipId::AssetId(asset_id) => {
108                assert_eq!(asset_id.chain_id().namespace(), "eip155");
109                assert_eq!(asset_id.chain_id().reference(), "1");
110                assert_eq!(asset_id.namespace(), "erc20");
111                assert_eq!(
112                    asset_id.reference(),
113                    "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
114                );
115            }
116            _ => panic!("Expected AssetId"),
117        }
118    }
119
120    #[test]
121    fn test_parse_invalid() {
122        assert!(parse("").is_err());
123        assert!(parse("invalid").is_err());
124        assert!(parse("foo:bar:baz:qux").is_err());
125    }
126
127    #[test]
128    fn test_caip_id_to_string() {
129        let chain_id = ChainId::from_str("eip155:1").unwrap();
130        let account_id =
131            AccountId::from_str("eip155:1:0x4b20993Bc481177ec7E8f571ceCaE8A9e22C02db").unwrap();
132        let asset_id =
133            AssetId::from_str("eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap();
134
135        let chain_caip = CaipId::ChainId(chain_id);
136        let account_caip = CaipId::AccountId(account_id);
137        let asset_caip = CaipId::AssetId(asset_id);
138
139        assert_eq!(chain_caip.to_string(), "eip155:1");
140        assert_eq!(
141            account_caip.to_string(),
142            "eip155:1:0x4b20993Bc481177ec7E8f571ceCaE8A9e22C02db"
143        );
144        assert_eq!(
145            asset_caip.to_string(),
146            "eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
147        );
148    }
149}