use core::fmt;
use core::str::FromStr;
use curve25519_dalek::edwards::CompressedEdwardsY;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use sha2::{Digest, Sha256};
use crate::error::{Error, Result};
const PDA_MARKER: &[u8] = b"ProgramDerivedAddress";
pub const MAX_SEED_LEN: usize = 32;
pub const MAX_SEEDS: usize = 16;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct Pubkey(pub(crate) [u8; 32]);
impl Pubkey {
pub const fn new_from_array(bytes: [u8; 32]) -> Self {
Pubkey(bytes)
}
pub fn from_base58(s: &str) -> Result<Self> {
if s.is_empty() || s.len() > 44 {
return Err(Error::InvalidPubkey(truncate_for_error(s)));
}
let mut out = [0u8; 32];
match bs58::decode(s).onto(&mut out) {
Ok(32) => Ok(Pubkey(out)),
_ => Err(Error::InvalidPubkey(truncate_for_error(s))),
}
}
pub const fn as_bytes(&self) -> &[u8; 32] {
&self.0
}
pub const fn to_bytes(self) -> [u8; 32] {
self.0
}
pub fn to_base58(&self) -> String {
bs58::encode(self.0).into_string()
}
pub fn abbreviated(&self) -> String {
let full = self.to_base58();
if full.len() <= 12 {
return full;
}
format!("{}…{}", &full[..6], &full[full.len() - 4..])
}
pub const fn zeroed() -> Self {
Pubkey([0u8; 32])
}
pub fn is_zeroed(&self) -> bool {
self.0 == [0u8; 32]
}
pub fn is_on_curve(&self) -> bool {
CompressedEdwardsY(self.0).decompress().is_some()
}
pub fn create_program_address(seeds: &[&[u8]], program_id: &Pubkey) -> Result<Pubkey> {
if seeds.len() > MAX_SEEDS {
return Err(Error::InvalidSeeds("too many seeds"));
}
if seeds.iter().any(|s| s.len() > MAX_SEED_LEN) {
return Err(Error::InvalidSeeds("seed longer than 32 bytes"));
}
let mut hasher = Sha256::new();
for seed in seeds {
hasher.update(seed);
}
hasher.update(program_id.0);
hasher.update(PDA_MARKER);
let hash: [u8; 32] = hasher.finalize().into();
let candidate = Pubkey(hash);
if candidate.is_on_curve() {
return Err(Error::InvalidSeeds("derived address is on the curve"));
}
Ok(candidate)
}
pub fn find_program_address(seeds: &[&[u8]], program_id: &Pubkey) -> Result<(Pubkey, u8)> {
for bump in (0u8..=255).rev() {
let bump_seed = [bump];
let mut all: Vec<&[u8]> = Vec::with_capacity(seeds.len() + 1);
all.extend_from_slice(seeds);
all.push(&bump_seed);
match Pubkey::create_program_address(&all, program_id) {
Ok(pk) => return Ok((pk, bump)),
Err(Error::InvalidSeeds("derived address is on the curve")) => continue,
Err(e) => return Err(e),
}
}
Err(Error::InvalidSeeds("no viable bump seed"))
}
}
fn truncate_for_error(s: &str) -> String {
let cleaned: String = s.chars().filter(|c| !c.is_control()).take(48).collect();
cleaned
}
impl fmt::Display for Pubkey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.to_base58())
}
}
impl fmt::Debug for Pubkey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.to_base58())
}
}
impl FromStr for Pubkey {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
Pubkey::from_base58(s)
}
}
impl From<[u8; 32]> for Pubkey {
fn from(b: [u8; 32]) -> Self {
Pubkey(b)
}
}
impl Serialize for Pubkey {
fn serialize<S: Serializer>(&self, s: S) -> core::result::Result<S::Ok, S::Error> {
s.serialize_str(&self.to_base58())
}
}
impl<'de> Deserialize<'de> for Pubkey {
fn deserialize<D: Deserializer<'de>>(d: D) -> core::result::Result<Self, D::Error> {
let s = String::deserialize(d)?;
Pubkey::from_base58(&s).map_err(serde::de::Error::custom)
}
}
pub mod ids {
use super::Pubkey;
pub const SYSTEM_PROGRAM: Pubkey = Pubkey([0; 32]);
pub const TOKEN_PROGRAM: Pubkey = Pubkey([
6, 221, 246, 225, 215, 101, 161, 147, 217, 203, 225, 70, 206, 235, 121, 172, 28, 180, 133,
237, 95, 91, 55, 145, 58, 140, 245, 133, 126, 255, 0, 169,
]);
pub const TOKEN_2022_PROGRAM: Pubkey = Pubkey([
6, 221, 246, 225, 238, 117, 143, 222, 24, 66, 93, 188, 228, 108, 205, 218, 182, 26, 252,
77, 131, 185, 13, 39, 254, 189, 249, 40, 216, 161, 139, 252,
]);
pub const ASSOCIATED_TOKEN_PROGRAM: Pubkey = Pubkey([
140, 151, 37, 143, 78, 36, 137, 241, 187, 61, 16, 41, 20, 142, 13, 131, 11, 90, 19, 153,
218, 255, 16, 132, 4, 142, 123, 216, 219, 233, 248, 89,
]);
pub const MEMO_PROGRAM: Pubkey = Pubkey([
5, 74, 83, 90, 153, 41, 33, 6, 77, 36, 232, 113, 96, 218, 56, 124, 124, 53, 181, 221, 188,
146, 187, 129, 228, 31, 168, 64, 65, 5, 68, 141,
]);
pub const METAPLEX_METADATA_PROGRAM: Pubkey = Pubkey([
11, 112, 101, 177, 227, 209, 124, 69, 56, 157, 82, 127, 107, 4, 195, 205, 88, 184, 108,
115, 26, 160, 253, 181, 73, 182, 209, 188, 3, 248, 41, 70,
]);
pub const COMPUTE_BUDGET_PROGRAM: Pubkey = Pubkey([
3, 6, 70, 111, 229, 33, 23, 50, 255, 236, 173, 186, 114, 195, 155, 231, 188, 140, 229, 187,
197, 247, 18, 107, 44, 67, 155, 58, 64, 0, 0, 0,
]);
pub const SYSVAR_RENT: Pubkey = Pubkey([
6, 167, 213, 23, 25, 44, 92, 81, 33, 140, 201, 76, 61, 74, 241, 127, 88, 218, 238, 8, 155,
161, 253, 68, 227, 219, 217, 138, 0, 0, 0, 0,
]);
pub const SYSVAR_RECENT_BLOCKHASHES: Pubkey = Pubkey([
6, 167, 213, 23, 25, 44, 86, 142, 224, 138, 132, 95, 115, 210, 151, 136, 207, 3, 92, 49,
69, 178, 26, 179, 68, 216, 6, 46, 169, 64, 0, 0,
]);
pub const NATIVE_MINT: Pubkey = Pubkey([
6, 155, 136, 87, 254, 171, 129, 132, 251, 104, 127, 99, 70, 24, 192, 53, 218, 196, 57, 220,
26, 235, 59, 85, 152, 160, 240, 0, 0, 0, 0, 1,
]);
pub const INCINERATOR: Pubkey = Pubkey([
0, 51, 144, 114, 141, 52, 17, 96, 121, 189, 201, 17, 191, 255, 0, 219, 212, 77, 46, 205,
204, 247, 156, 166, 225, 0, 56, 225, 0, 0, 0, 0,
]);
}