cardano_sdk/cardano/
address.rs1use crate::{Credential, NetworkId, pallas};
6use anyhow::anyhow;
7use std::{cmp::Ordering, fmt, marker::PhantomData, str::FromStr, sync::Arc};
8
9pub mod kind;
10pub use kind::IsAddressKind;
11
12#[derive(Debug, Clone, PartialEq, Eq)]
72pub struct Address<T: IsAddressKind>(Arc<AddressKind>, PhantomData<T>);
73
74impl<T: IsAddressKind + Eq> PartialOrd for Address<T> {
75 fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
76 Some(self.cmp(rhs))
77 }
78}
79
80impl<T: IsAddressKind + Eq> Ord for Address<T> {
81 fn cmp(&self, rhs: &Self) -> Ordering {
82 <Vec<u8>>::from(self).cmp(&<Vec<u8>>::from(rhs))
83 }
84}
85
86#[derive(Debug, Clone, PartialEq, Eq)]
87enum AddressKind {
88 Byron(pallas::ByronAddress),
89 Shelley(pallas::ShelleyAddress),
90}
91
92impl Address<kind::Shelley> {
95 pub fn new(network: NetworkId, payment: Credential) -> Self {
97 Self::from(pallas::ShelleyAddress::new(
98 pallas::Network::from(network),
99 pallas::ShelleyPaymentPart::from(payment),
100 pallas::ShelleyDelegationPart::Null,
101 ))
102 }
103
104 pub fn with_delegation(mut self, delegation: Credential) -> Self {
106 self = Self::from(pallas::ShelleyAddress::new(
107 pallas::Network::from(self.network_id()),
108 pallas::ShelleyPaymentPart::from(self.payment()),
109 pallas::ShelleyDelegationPart::from(delegation),
110 ));
111
112 self
113 }
114}
115
116impl Address<kind::Shelley> {
119 fn cast(&self) -> &pallas::ShelleyAddress {
120 match self.0.as_ref() {
121 AddressKind::Shelley(shelley) => shelley,
122 _ => unreachable!(),
123 }
124 }
125
126 pub fn network_id(&self) -> NetworkId {
130 NetworkId::from(self.cast().network())
131 }
132
133 pub fn payment(&self) -> Credential {
134 Credential::from(self.cast().payment())
135 }
136
137 pub fn delegation(&self) -> Option<Credential> {
138 Credential::try_from(self.cast().delegation()).ok()
139 }
140}
141
142impl Default for Address<kind::Any> {
145 fn default() -> Self {
146 Self::from(
147 Address::new(NetworkId::MAINNET, Credential::default())
148 .with_delegation(Credential::default()),
149 )
150 }
151}
152
153impl<T: IsAddressKind> Address<T> {
156 pub fn is_byron(&self) -> bool {
181 matches!(self.0.as_ref(), &AddressKind::Byron(..))
182 }
183
184 pub fn as_byron(&self) -> Option<Address<kind::Byron>> {
187 if self.is_byron() {
188 return Some(Address(self.0.clone(), PhantomData));
189 }
190
191 None
192 }
193
194 pub fn is_shelley(&self) -> bool {
219 matches!(&self.0.as_ref(), AddressKind::Shelley(..))
220 }
221
222 pub fn as_shelley(&self) -> Option<Address<kind::Shelley>> {
225 if self.is_shelley() {
226 return Some(Address(self.0.clone(), PhantomData));
227 }
228
229 None
230 }
231}
232
233impl From<pallas::ByronAddress> for Address<kind::Byron> {
236 fn from(byron_address: pallas::ByronAddress) -> Self {
237 Self(Arc::new(AddressKind::Byron(byron_address)), PhantomData)
238 }
239}
240
241impl From<pallas::ShelleyAddress> for Address<kind::Shelley> {
242 fn from(shelley_address: pallas::ShelleyAddress) -> Self {
243 Self(Arc::new(AddressKind::Shelley(shelley_address)), PhantomData)
244 }
245}
246
247impl From<Address<kind::Byron>> for Address<kind::Any> {
248 fn from(byron_address: Address<kind::Byron>) -> Self {
249 Self(byron_address.0, PhantomData)
250 }
251}
252
253impl From<Address<kind::Shelley>> for Address<kind::Any> {
254 fn from(shelley_address: Address<kind::Shelley>) -> Self {
255 Self(shelley_address.0, PhantomData)
256 }
257}
258
259impl TryFrom<pallas::Address> for Address<kind::Any> {
260 type Error = anyhow::Error;
261
262 fn try_from(address: pallas::Address) -> anyhow::Result<Self> {
263 match address {
264 pallas_addresses::Address::Byron(byron) => Ok(Address::<kind::Any>(
265 Arc::new(AddressKind::Byron(byron)),
266 PhantomData,
267 )),
268 pallas_addresses::Address::Shelley(shelley) => Ok(Address::<kind::Any>(
269 Arc::new(AddressKind::Shelley(shelley)),
270 PhantomData,
271 )),
272 pallas_addresses::Address::Stake(_) => {
273 Err(anyhow!("found stake address masquerading as address"))
274 }
275 }
276 }
277}
278
279impl TryFrom<pallas::Address> for Address<kind::Shelley> {
280 type Error = anyhow::Error;
281
282 fn try_from(address: pallas::Address) -> anyhow::Result<Self> {
283 match address {
284 pallas_addresses::Address::Shelley(shelley) => Ok(Address::<kind::Shelley>(
285 Arc::new(AddressKind::Shelley(shelley)),
286 PhantomData,
287 )),
288 pallas_addresses::Address::Byron(_) | pallas_addresses::Address::Stake(_) => {
289 Err(anyhow!("not a shelley address"))
290 }
291 }
292 }
293}
294
295impl<T: IsAddressKind> TryFrom<&str> for Address<T>
296where
297 Address<T>: TryFrom<pallas::Address, Error = anyhow::Error>,
298{
299 type Error = anyhow::Error;
300
301 fn try_from(text: &str) -> anyhow::Result<Self> {
302 Self::try_from(pallas::Address::from_str(text).map_err(|e| anyhow!(e))?)
303 }
304}
305
306impl<T: IsAddressKind> std::str::FromStr for Address<T>
307where
308 Address<T>: for<'a> TryFrom<&'a str, Error = anyhow::Error>,
309{
310 type Err = anyhow::Error;
311
312 fn from_str(s: &str) -> anyhow::Result<Address<T>> {
313 Self::try_from(s)
314 }
315}
316
317impl<T: IsAddressKind> TryFrom<&[u8]> for Address<T>
318where
319 Address<T>: TryFrom<pallas::Address, Error = anyhow::Error>,
320{
321 type Error = anyhow::Error;
322
323 fn try_from(bytes: &[u8]) -> anyhow::Result<Self> {
324 Self::try_from(pallas::Address::from_bytes(bytes).map_err(|e| anyhow!(e))?)
325 }
326}
327
328impl<T: IsAddressKind> fmt::Display for Address<T> {
331 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
332 match self.0.as_ref() {
333 AddressKind::Byron(byron) => f.write_str(byron.to_base58().as_str()),
334 AddressKind::Shelley(shelley) => f.write_str(
335 shelley
336 .to_bech32()
337 .expect("failed to convert to bech32!?")
338 .as_str(),
339 ),
340 }
341 }
342}
343
344impl<T: IsAddressKind> From<&Address<T>> for Vec<u8> {
345 fn from(address: &Address<T>) -> Self {
346 match address.0.as_ref() {
347 AddressKind::Byron(byron) => byron.to_vec(),
348 AddressKind::Shelley(shelley) => shelley.to_vec(),
349 }
350 }
351}
352
353#[cfg(any(test, feature = "test-utils"))]
356pub mod tests {
357
358 pub mod generators {
361 use crate::{Address, address::kind::*, any};
362 use proptest::{option, prelude::*};
363
364 prop_compose! {
365 pub fn address_shelley()(
366 network_id in any::network_id(),
367 payment_credential in any::credential(),
368 delegation_credential_opt in option::of(any::credential()),
369 ) -> Address<Shelley> {
370 let address = Address::new(network_id, payment_credential);
371
372 if let Some(delegation_credential) = delegation_credential_opt {
373 return address.with_delegation(delegation_credential)
374 }
375
376 address
377 }
378 }
379 }
380}