algokit_transact/
address.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
//! Algorand address handling and manipulation.
//!
//! This module provides functionality for working with Algorand addresses,
//! including creation, validation, encoding, and decoding. Algorand addresses
//! are base32-encoded strings that represent a public key with a checksum.

use crate::constants::{
    Byte32, ALGORAND_ADDRESS_LENGTH, ALGORAND_CHECKSUM_BYTE_LENGTH, ALGORAND_PUBLIC_KEY_BYTE_LENGTH,
};
use crate::error::AlgoKitTransactError;
use crate::utils::pub_key_to_checksum;
use serde::{Deserialize, Serialize};
use serde_with::{serde_as, Bytes};
use std::fmt::{Display, Formatter, Result as FmtResult};
use std::str::FromStr;

/// Represents an address.
///
/// An Algorand address is a 32-byte public key with a 4-byte checksum,
/// typically represented as a 58-character base32-encoded string.
/// This struct encapsulates the underlying public key and provides
/// methods for creating, validating, and converting human-readable addresses.
#[serde_as]
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Default)]
#[serde(transparent)]
pub struct Address {
    /// The 32-byte Ed25519 public key associated with this address.
    #[serde_as(as = "Bytes")]
    pub pub_key: Byte32,
}

impl Address {
    /// Creates a new Address from a 32-byte public key.
    ///
    /// # Parameters
    /// * `pub_key` - The 32-byte Ed25519 public key
    ///
    /// # Returns
    /// A new Address instance containing the provided public key.
    pub fn from_pubkey(pub_key: &Byte32) -> Self {
        Address { pub_key: *pub_key }
    }

    /// Calculates the 4-byte checksum for this address's public key.
    ///
    /// # Returns
    /// A 4-byte array containing the checksum.
    pub fn checksum(&self) -> [u8; ALGORAND_CHECKSUM_BYTE_LENGTH] {
        pub_key_to_checksum(&self.pub_key)
    }
}

impl FromStr for Address {
    type Err = AlgoKitTransactError;

    /// Creates a new Address from a base32-encoded string.
    ///
    /// # Parameters
    /// * `s` - A 58-character base32-encoded Algorand address string
    ///
    /// # Returns
    /// The Address or an error if the string is invalid (wrong length, invalid base32,
    /// invalid format, or checksum mismatch, etc.).
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if s.len() != ALGORAND_ADDRESS_LENGTH {
            return Err(AlgoKitTransactError::InvalidAddress(
                "address length is not 58".to_string(),
            ));
        }
        let decoded = base32::decode(base32::Alphabet::Rfc4648 { padding: false }, s)
            .expect("decoded value should exist");

        let pub_key: [u8; ALGORAND_PUBLIC_KEY_BYTE_LENGTH] = decoded
            [..ALGORAND_PUBLIC_KEY_BYTE_LENGTH]
            .try_into()
            .map_err(|_| {
                AlgoKitTransactError::InvalidAddress(
                    "could not decode address into 32-byte public key".to_string(),
                )
            })?;

        let checksum: [u8; 4] = decoded[ALGORAND_PUBLIC_KEY_BYTE_LENGTH..]
            .try_into()
            .map_err(|_| {
                AlgoKitTransactError::InvalidAddress(
                    "could not get 4-byte checksum from decoded address".to_string(),
                )
            })?;

        let computed_checksum = pub_key_to_checksum(&pub_key);

        if computed_checksum != checksum {
            return Err(AlgoKitTransactError::InvalidAddress(
                "checksum is invalid".to_string(),
            ));
        }

        Ok(Self { pub_key })
    }
}

impl Display for Address {
    /// Formats the address as a base32-encoded string.
    ///
    /// # Parameters
    /// * `f` - The formatter
    ///
    /// # Returns
    /// A formatting result with a string containing the base32-encoded address or error
    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
        let mut address_bytes =
            [0u8; ALGORAND_PUBLIC_KEY_BYTE_LENGTH + ALGORAND_CHECKSUM_BYTE_LENGTH];

        address_bytes[..ALGORAND_PUBLIC_KEY_BYTE_LENGTH].copy_from_slice(&self.pub_key);

        let checksum = self.checksum();
        address_bytes[ALGORAND_PUBLIC_KEY_BYTE_LENGTH..].copy_from_slice(&checksum);

        let result = base32::encode(base32::Alphabet::Rfc4648 { padding: false }, &address_bytes);
        write!(f, "{}", result)
    }
}