foundation_urtypes/registry/
coininfo.rs1use minicbor::{
5 data::Tag, data::Type, decode::Error, encode::Write, Decode, Decoder, Encode, Encoder,
6};
7
8#[doc(alias("crypto-coininfo"))]
10#[derive(Debug, Clone, PartialEq, Eq, Hash)]
11pub struct CoinInfo {
12 pub coin_type: CoinType,
14 pub network: u64,
20}
21
22impl CoinInfo {
23 pub const TAG: Tag = Tag::new(40305);
25
26 pub const NETWORK_MAINNET: u64 = 0;
28
29 pub const NETWORK_BTC_TESTNET: u64 = 1;
31
32 pub const BTC_MAINNET: Self = Self {
34 coin_type: CoinType::BTC,
35 network: Self::NETWORK_MAINNET,
36 };
37
38 pub const fn new(coin_type: CoinType, network: u64) -> Self {
40 Self { coin_type, network }
41 }
42
43 pub fn is_default(&self) -> bool {
44 self.coin_type == CoinType::BTC && self.network == Self::NETWORK_MAINNET
45 }
46}
47
48impl<'b, C> Decode<'b, C> for CoinInfo {
49 fn decode(d: &mut Decoder<'b>, ctx: &mut C) -> Result<Self, Error> {
50 let mut coin_type = None;
51 let mut network = None;
52
53 let mut len = d.map()?;
54 loop {
55 match len {
56 Some(0) => break,
57 Some(n) => len = Some(n - 1),
58 None => {
59 if d.datatype()? == Type::Break {
60 break;
61 }
62 }
63 }
64
65 match d.u32()? {
66 1 => coin_type = Some(CoinType::decode(d, ctx)?),
67 2 => network = Some(d.u64()?),
68 _ => return Err(Error::message("unknown map entry")),
69 }
70 }
71
72 Ok(Self {
73 coin_type: coin_type.unwrap_or(CoinType::BTC),
74 network: network.unwrap_or(0),
75 })
76 }
77}
78
79impl<C> Encode<C> for CoinInfo {
80 fn encode<W: Write>(
81 &self,
82 e: &mut Encoder<W>,
83 ctx: &mut C,
84 ) -> Result<(), minicbor::encode::Error<W::Error>> {
85 let is_not_default_coin_type = self.coin_type != CoinType::BTC;
86 let is_not_default_network = self.network != 0;
87 let len = is_not_default_coin_type as u64 + is_not_default_network as u64;
88
89 e.map(len)?;
90
91 if is_not_default_coin_type {
92 e.u8(1)?;
93 self.coin_type.encode(e, ctx)?;
94 }
95
96 if is_not_default_network {
97 e.u8(2)?.u64(self.network)?;
98 }
99
100 Ok(())
101 }
102}
103
104#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
110pub struct CoinType(pub(crate) u32);
111
112impl CoinType {
113 pub const BTC: Self = CoinType(0x00);
114
115 pub fn new(value: u32) -> Self {
116 Self(value)
117 }
118
119 pub fn get(self) -> u32 {
121 self.0
122 }
123}
124
125impl<'b, C> Decode<'b, C> for CoinType {
126 fn decode(d: &mut Decoder<'b>, _ctx: &mut C) -> Result<Self, Error> {
127 let n = d.u32()?;
128 if n >= 1 << 31 {
129 return Err(Error::message("coin type out of range"));
130 }
131
132 Ok(CoinType(n))
133 }
134}
135
136impl<C> Encode<C> for CoinType {
137 fn encode<W: Write>(
138 &self,
139 e: &mut Encoder<W>,
140 _ctx: &mut C,
141 ) -> Result<(), minicbor::encode::Error<W::Error>> {
142 e.u32(self.get())?;
143 Ok(())
144 }
145}
146
147impl From<u32> for CoinType {
148 fn from(n: u32) -> Self {
149 CoinType(n)
150 }
151}
152
153impl From<CoinType> for u32 {
154 fn from(coin_type: CoinType) -> Self {
155 coin_type.get()
156 }
157}
158
159#[cfg(test)]
160mod tests {
161 use super::*;
162
163 #[test]
165 fn test_crypto_coininfo_roundtrip() {
166 let crypto_coininfo = CoinInfo::BTC_MAINNET;
167 let cbor = minicbor::to_vec(&crypto_coininfo).unwrap();
168 let decoded = minicbor::decode(&cbor).unwrap();
169 assert_eq!(crypto_coininfo, decoded);
170 }
171
172 #[test]
174 fn test_coin_type_roundtrip() {
175 let coin_type = CoinType::BTC;
176 let cbor = minicbor::to_vec(coin_type).unwrap();
177 assert_eq!(cbor, &[0x00]);
178 let decoded = minicbor::decode(&cbor).unwrap();
179 assert_eq!(coin_type, decoded);
180 }
181}