pub const PDA_NAMESPACE_KEYS: &[&str] = &["seeds", "zero_copy"];
pub const TOKEN_NAMESPACE_KEYS: &[&str] = &["seeds", "mint", "owner", "bump", "owner_seeds"];
pub const ASSOCIATED_TOKEN_NAMESPACE_KEYS: &[&str] = &["authority", "mint", "idempotent"];
pub const MINT_NAMESPACE_KEYS: &[&str] = &[
"signer",
"authority",
"decimals",
"seeds",
"bump",
"freeze_authority",
"authority_seeds",
"authority_bump",
"name",
"symbol",
"uri",
"update_authority",
"additional_metadata",
];
pub const STANDALONE_KEYWORDS: &[&str] = &["init"];
pub const SHORTHAND_KEYS_BY_NAMESPACE: &[(&str, &[&str])] = &[
("token", &["mint", "owner", "bump"]), ("associated_token", &["authority", "mint"]),
];
pub const BOOLEAN_FLAG_KEYS_BY_NAMESPACE: &[(&str, &[&str])] =
&[("associated_token", &["idempotent"])];
#[inline]
pub fn is_boolean_flag_key(namespace: &str, key: &str) -> bool {
for (ns, keys) in BOOLEAN_FLAG_KEYS_BY_NAMESPACE {
if *ns == namespace {
return keys.contains(&key);
}
}
false
}
#[inline]
pub fn is_standalone_keyword(keyword: &str) -> bool {
STANDALONE_KEYWORDS.contains(&keyword)
}
#[inline]
pub fn is_shorthand_key(namespace: &str, key: &str) -> bool {
for (ns, keys) in SHORTHAND_KEYS_BY_NAMESPACE {
if *ns == namespace {
return keys.contains(&key);
}
}
false
}
pub fn valid_keys_for_namespace(namespace: &str) -> &'static [&'static str] {
match namespace {
"pda" => PDA_NAMESPACE_KEYS,
"token" => TOKEN_NAMESPACE_KEYS,
"associated_token" => ASSOCIATED_TOKEN_NAMESPACE_KEYS,
"mint" => MINT_NAMESPACE_KEYS,
_ => &[],
}
}
pub fn validate_namespaced_key(namespace: &str, key: &str) -> Result<(), String> {
let valid_keys = valid_keys_for_namespace(namespace);
if valid_keys.is_empty() {
return Err(format!(
"Unknown namespace `{}`. Expected: pda, token, associated_token, or mint",
namespace
));
}
if !valid_keys.contains(&key) {
return Err(format!(
"Unknown key `{}` in `{}::` namespace. Allowed: {}",
key,
namespace,
valid_keys.join(", ")
));
}
Ok(())
}
pub fn unknown_key_error(namespace: &str, key: &str) -> String {
let valid = valid_keys_for_namespace(namespace);
if valid.is_empty() {
format!(
"Unknown namespace `{}`. Expected: pda, token, associated_token, or mint",
namespace
)
} else {
format!(
"Unknown key `{}` in #[light_account({}, ...)]. Allowed for `{}::`: {}",
key,
namespace,
namespace,
valid.join(", ")
)
}
}
pub fn missing_namespace_error(key: &str, account_type: &str) -> String {
format!(
"Missing namespace prefix for `{}`. Use `{}::{}` instead of just `{}`",
key, account_type, key, key
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pda_namespace_keys() {
assert!(PDA_NAMESPACE_KEYS.contains(&"seeds"));
assert!(PDA_NAMESPACE_KEYS.contains(&"zero_copy"));
assert!(!PDA_NAMESPACE_KEYS.contains(&"unknown"));
}
#[test]
fn test_token_namespace_keys() {
assert!(TOKEN_NAMESPACE_KEYS.contains(&"seeds"));
assert!(TOKEN_NAMESPACE_KEYS.contains(&"mint"));
assert!(TOKEN_NAMESPACE_KEYS.contains(&"owner"));
assert!(TOKEN_NAMESPACE_KEYS.contains(&"bump"));
assert!(!TOKEN_NAMESPACE_KEYS.contains(&"authority")); assert!(!TOKEN_NAMESPACE_KEYS.contains(&"unknown"));
}
#[test]
fn test_associated_token_namespace_keys() {
assert!(ASSOCIATED_TOKEN_NAMESPACE_KEYS.contains(&"authority"));
assert!(ASSOCIATED_TOKEN_NAMESPACE_KEYS.contains(&"mint"));
assert!(ASSOCIATED_TOKEN_NAMESPACE_KEYS.contains(&"idempotent"));
assert!(!ASSOCIATED_TOKEN_NAMESPACE_KEYS.contains(&"bump")); assert!(!ASSOCIATED_TOKEN_NAMESPACE_KEYS.contains(&"owner")); assert!(!ASSOCIATED_TOKEN_NAMESPACE_KEYS.contains(&"unknown"));
}
#[test]
fn test_boolean_flag_keys() {
assert!(is_boolean_flag_key("associated_token", "idempotent"));
assert!(!is_boolean_flag_key("associated_token", "authority"));
assert!(!is_boolean_flag_key("associated_token", "mint"));
assert!(!is_boolean_flag_key("token", "idempotent"));
assert!(!is_boolean_flag_key("mint", "idempotent"));
}
#[test]
fn test_mint_namespace_keys() {
assert!(MINT_NAMESPACE_KEYS.contains(&"signer")); assert!(MINT_NAMESPACE_KEYS.contains(&"authority"));
assert!(MINT_NAMESPACE_KEYS.contains(&"decimals"));
assert!(MINT_NAMESPACE_KEYS.contains(&"seeds")); assert!(MINT_NAMESPACE_KEYS.contains(&"bump")); assert!(MINT_NAMESPACE_KEYS.contains(&"freeze_authority"));
assert!(MINT_NAMESPACE_KEYS.contains(&"authority_seeds"));
assert!(MINT_NAMESPACE_KEYS.contains(&"authority_bump"));
assert!(MINT_NAMESPACE_KEYS.contains(&"name"));
assert!(MINT_NAMESPACE_KEYS.contains(&"symbol"));
assert!(MINT_NAMESPACE_KEYS.contains(&"uri"));
assert!(MINT_NAMESPACE_KEYS.contains(&"update_authority"));
assert!(MINT_NAMESPACE_KEYS.contains(&"additional_metadata"));
}
#[test]
fn test_standalone_keywords() {
assert!(is_standalone_keyword("init"));
assert!(!is_standalone_keyword("mint"));
assert!(!is_standalone_keyword("zero_copy"));
assert!(!is_standalone_keyword("token"));
assert!(!is_standalone_keyword("associated_token"));
assert!(!is_standalone_keyword("authority"));
}
#[test]
fn test_shorthand_keys() {
assert!(is_shorthand_key("token", "mint"));
assert!(is_shorthand_key("token", "owner"));
assert!(is_shorthand_key("token", "bump"));
assert!(!is_shorthand_key("token", "seeds"));
assert!(is_shorthand_key("associated_token", "authority"));
assert!(is_shorthand_key("associated_token", "mint"));
assert!(!is_shorthand_key("mint", "signer"));
assert!(!is_shorthand_key("mint", "authority"));
}
#[test]
fn test_valid_keys_for_namespace() {
let pda_kw = valid_keys_for_namespace("pda");
assert_eq!(pda_kw, PDA_NAMESPACE_KEYS);
let token_kw = valid_keys_for_namespace("token");
assert_eq!(token_kw, TOKEN_NAMESPACE_KEYS);
let ata_kw = valid_keys_for_namespace("associated_token");
assert_eq!(ata_kw, ASSOCIATED_TOKEN_NAMESPACE_KEYS);
let mint_kw = valid_keys_for_namespace("mint");
assert_eq!(mint_kw, MINT_NAMESPACE_KEYS);
let unknown_kw = valid_keys_for_namespace("unknown");
assert!(unknown_kw.is_empty());
}
#[test]
fn test_validate_namespaced_key() {
assert!(validate_namespaced_key("token", "seeds").is_ok());
assert!(validate_namespaced_key("token", "mint").is_ok());
assert!(validate_namespaced_key("associated_token", "authority").is_ok());
assert!(validate_namespaced_key("mint", "signer").is_ok());
assert!(validate_namespaced_key("mint", "decimals").is_ok());
assert!(validate_namespaced_key("token", "invalid").is_err());
assert!(validate_namespaced_key("token", "authority").is_err()); assert!(validate_namespaced_key("unknown_namespace", "key").is_err());
}
#[test]
fn test_unknown_key_error() {
let error = unknown_key_error("token", "invalid");
assert!(error.contains("invalid"));
assert!(error.contains("token"));
assert!(error.contains("seeds"));
let error = unknown_key_error("unknown", "key");
assert!(error.contains("Unknown namespace"));
}
#[test]
fn test_missing_namespace_error() {
let error = missing_namespace_error("authority", "token");
assert!(error.contains("token::authority"));
assert!(error.contains("Missing namespace prefix"));
}
}