|
4 | 4 | #![cfg_attr(feature = "frozen-abi", feature(min_specialization))] |
5 | 5 | #![allow(clippy::arithmetic_side_effects)] |
6 | 6 |
|
| 7 | +#[cfg(all(feature = "serde", not(feature = "std")))] |
| 8 | +extern crate alloc; |
| 9 | + |
7 | 10 | #[cfg(any(feature = "std", target_arch = "wasm32"))] |
8 | 11 | extern crate std; |
9 | 12 | #[cfg(feature = "dev-context-only-utils")] |
@@ -156,11 +159,27 @@ impl From<u64> for PubkeyError { |
156 | 159 | borsh(crate = "borsh") |
157 | 160 | )] |
158 | 161 | #[cfg_attr(all(feature = "borsh", feature = "std"), derive(BorshSchema))] |
| 162 | +#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] |
159 | 163 | #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] |
160 | 164 | #[cfg_attr(feature = "bytemuck", derive(Pod, Zeroable))] |
161 | 165 | #[derive(Clone, Copy, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] |
162 | 166 | #[cfg_attr(feature = "dev-context-only-utils", derive(Arbitrary))] |
163 | | -pub struct Pubkey(pub(crate) [u8; 32]); |
| 167 | +#[cfg_attr(feature = "serde", serde(transparent))] |
| 168 | +pub struct Pubkey( |
| 169 | + #[cfg_attr( |
| 170 | + feature = "serde", |
| 171 | + serde(serialize_with = "serialize", deserialize_with = "deserialize") |
| 172 | + )] |
| 173 | + #[cfg_attr( |
| 174 | + feature = "schemars", |
| 175 | + schemars( |
| 176 | + with = "std::string::String", |
| 177 | + title = "SolanaPubkey", |
| 178 | + range(min = 32, max = 44) |
| 179 | + ) |
| 180 | + )] |
| 181 | + pub(crate) [u8; 32], |
| 182 | +); |
164 | 183 |
|
165 | 184 | impl solana_sanitize::Sanitize for Pubkey {} |
166 | 185 |
|
@@ -894,6 +913,35 @@ macro_rules! impl_borsh_serialize { |
894 | 913 | #[cfg(feature = "borsh")] |
895 | 914 | impl_borsh_serialize!(borsh0_10); |
896 | 915 |
|
| 916 | +#[cfg(feature = "serde")] |
| 917 | +fn serialize<S>(bytes: &[u8; 32], serializer: S) -> Result<S::Ok, S::Error> |
| 918 | +where |
| 919 | + S: serde::Serializer, |
| 920 | +{ |
| 921 | + let encoded = bs58::encode(bytes).into_string(); |
| 922 | + serializer.serialize_str(&encoded) |
| 923 | +} |
| 924 | + |
| 925 | +#[cfg(feature = "serde")] |
| 926 | +fn deserialize<'de, D>(deserializer: D) -> Result<[u8; 32], D::Error> |
| 927 | +where |
| 928 | + D: serde::Deserializer<'de>, |
| 929 | +{ |
| 930 | + #[cfg(feature = "std")] |
| 931 | + use std::{format, string::String}; |
| 932 | + |
| 933 | + #[cfg(not(feature = "std"))] |
| 934 | + use alloc::{format, string::String}; |
| 935 | + |
| 936 | + let s: String = serde::Deserialize::deserialize(deserializer) |
| 937 | + .map_err(|_| serde::de::Error::custom("Failed to read String"))?; |
| 938 | + let b = bs58::decode(s) |
| 939 | + .into_vec() |
| 940 | + .map_err(|x| serde::de::Error::custom(format!("Failed to decode base58 {x}")))?; |
| 941 | + b.try_into() |
| 942 | + .map_err(|_| serde::de::Error::custom("Invalid length")) |
| 943 | +} |
| 944 | + |
897 | 945 | #[cfg(all(target_arch = "wasm32", feature = "curve25519"))] |
898 | 946 | fn js_value_to_seeds_vec(array_of_uint8_arrays: &[JsValue]) -> Result<Vec<Vec<u8>>, JsValue> { |
899 | 947 | let vec_vec_u8 = array_of_uint8_arrays |
@@ -1416,4 +1464,30 @@ mod tests { |
1416 | 1464 | // Sanity check: ensure the pointer is the same. |
1417 | 1465 | assert_eq!(key.as_array().as_ptr(), key.0.as_ptr()); |
1418 | 1466 | } |
| 1467 | + |
| 1468 | + #[test] |
| 1469 | + #[cfg(feature = "serde")] |
| 1470 | + fn json() { |
| 1471 | + let pubkey = Pubkey::new_unique(); |
| 1472 | + let json = serde_json::to_string(&pubkey).unwrap(); |
| 1473 | + let display = pubkey.to_string(); |
| 1474 | + let back = serde_json::from_str::<Pubkey>(&json).unwrap(); |
| 1475 | + |
| 1476 | + assert_eq!(json, std::format!("\"{display}\"")); |
| 1477 | + assert_eq!(pubkey, back); |
| 1478 | + } |
| 1479 | + |
| 1480 | + #[test] |
| 1481 | + #[cfg(feature = "schemars")] |
| 1482 | + fn schemars() { |
| 1483 | + use schemars::{ |
| 1484 | + schema::{InstanceType, SingleOrVec}, |
| 1485 | + schema_for, |
| 1486 | + }; |
| 1487 | + let schema = schema_for!(Pubkey); |
| 1488 | + assert_eq!( |
| 1489 | + schema.schema.instance_type.unwrap(), |
| 1490 | + SingleOrVec::Single(std::boxed::Box::new(InstanceType::String)) |
| 1491 | + ); |
| 1492 | + } |
1419 | 1493 | } |
0 commit comments