algokit_transact/
address.rsuse crate::constants::Byte32;
use crate::error::AlgoKitTransactError;
use crate::utils::{hash, pub_key_to_checksum};
use crate::{
ALGORAND_ADDRESS_LENGTH, ALGORAND_CHECKSUM_BYTE_LENGTH, ALGORAND_PUBLIC_KEY_BYTE_LENGTH,
};
use serde::{Deserialize, Serialize};
use serde_with::{Bytes, serde_as};
use std::fmt::{Display, Formatter, Result as FmtResult};
use std::str::FromStr;
#[serde_as]
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq, Eq)]
#[serde(transparent)]
pub struct Address(#[serde_as(as = "Bytes")] pub Byte32);
impl Address {
pub fn as_bytes(&self) -> &Byte32 {
&self.0
}
pub fn from_app_id(app_id: &u64) -> Self {
let mut to_hash = b"appID".to_vec();
to_hash.extend_from_slice(&app_id.to_be_bytes());
Address(hash(&to_hash))
}
pub fn as_str(&self) -> String {
let mut buffer = [0u8; ALGORAND_PUBLIC_KEY_BYTE_LENGTH + ALGORAND_CHECKSUM_BYTE_LENGTH];
buffer[..ALGORAND_PUBLIC_KEY_BYTE_LENGTH].copy_from_slice(&self.0);
let checksum = self.checksum();
buffer[ALGORAND_PUBLIC_KEY_BYTE_LENGTH..].copy_from_slice(&checksum);
base32::encode(base32::Alphabet::Rfc4648 { padding: false }, &buffer)
}
pub fn checksum(&self) -> [u8; ALGORAND_CHECKSUM_BYTE_LENGTH] {
pub_key_to_checksum(&self.0)
}
}
impl FromStr for Address {
type Err = AlgoKitTransactError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.len() != ALGORAND_ADDRESS_LENGTH {
return Err(AlgoKitTransactError::InvalidAddress {
message: "Algorand address must be exactly 58 characters".into(),
});
}
let decoded_address = base32::decode(base32::Alphabet::Rfc4648 { padding: false }, s)
.ok_or_else(|| AlgoKitTransactError::InvalidAddress {
message: "Invalid base32 encoding for Algorand address".into(),
})?;
let pub_key: [u8; ALGORAND_PUBLIC_KEY_BYTE_LENGTH] = decoded_address
[..ALGORAND_PUBLIC_KEY_BYTE_LENGTH]
.try_into()
.map_err(|_| AlgoKitTransactError::InvalidAddress {
message: "Could not decode address into 32-byte public key".to_string(),
})?;
let checksum: [u8; ALGORAND_CHECKSUM_BYTE_LENGTH] = decoded_address
[ALGORAND_PUBLIC_KEY_BYTE_LENGTH..]
.try_into()
.map_err(|_| AlgoKitTransactError::InvalidAddress {
message: "Could not get 4-byte checksum from decoded address".to_string(),
})?;
if pub_key_to_checksum(&pub_key) != checksum {
return Err(AlgoKitTransactError::InvalidAddress {
message: "Checksum is invalid".to_string(),
});
}
Ok(Address(pub_key))
}
}
impl Display for Address {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
write!(f, "{}", self.as_str())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_from_app_id() {
let app_id = 123u64;
let address = Address::from_app_id(&app_id);
let address_str = address.to_string();
assert_eq!(
address_str,
"WRBMNT66ECE2AOYKM76YVWIJMBW6Z3XCQZOKG5BL7NISAQC2LBGEKTZLRM"
);
}
}