Add serde feature - token-2022 (#3291)
* Add serde to token-2022 * Add tests * Fix feature syntax * Formatting, enabling features * Make CI work, and some naming nit cleanup * Un-async tests Co-authored-by: jon wong <j@jnwng.com> Co-authored-by: Jon Cinque <jon.cinque@gmail.com>
This commit is contained in:
parent
b5cec79ee4
commit
f30ad1d752
|
@ -67,6 +67,14 @@ jobs:
|
|||
./token/twoxtx-setup.sh
|
||||
./ci/cargo-test-bpf.sh token/program-2022-test
|
||||
|
||||
- name: Build and test token-2022 with "serde" activated
|
||||
run: |
|
||||
cargo +"${{ env.RUST_STABLE }}" test \
|
||||
--manifest-path=token/program-2022/Cargo.toml \
|
||||
--features serde-traits \
|
||||
-- --nocapture
|
||||
exit 0
|
||||
|
||||
- name: Upload programs
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
|
|
|
@ -106,9 +106,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.52"
|
||||
version = "1.0.58"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84450d0b4a8bd1ba4144ce8ce718fbc5d071358b1e5384bace6536b3d1f2d5b3"
|
||||
checksum = "bb07d2053ccdbe10e2af2995a2f116c1330396493dc1269f6a91d0ae82e19704"
|
||||
|
||||
[[package]]
|
||||
name = "arbitrary"
|
||||
|
@ -733,7 +733,7 @@ dependencies = [
|
|||
"ansi_term",
|
||||
"atty",
|
||||
"bitflags",
|
||||
"strsim",
|
||||
"strsim 0.8.0",
|
||||
"textwrap",
|
||||
"unicode-width",
|
||||
"vec_map",
|
||||
|
@ -974,6 +974,41 @@ dependencies = [
|
|||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.13.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"darling_macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_core"
|
||||
version = "0.13.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"ident_case",
|
||||
"proc-macro2 1.0.36",
|
||||
"quote 1.0.14",
|
||||
"strsim 0.10.0",
|
||||
"syn 1.0.91",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.13.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote 1.0.14",
|
||||
"syn 1.0.91",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dashmap"
|
||||
version = "4.0.2"
|
||||
|
@ -1905,6 +1940,12 @@ dependencies = [
|
|||
"tokio-native-tls",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ident_case"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.1.5"
|
||||
|
@ -3964,9 +4005,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
|||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.136"
|
||||
version = "1.0.137"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789"
|
||||
checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
@ -3982,9 +4023,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.136"
|
||||
version = "1.0.137"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9"
|
||||
checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.36",
|
||||
"quote 1.0.14",
|
||||
|
@ -3993,9 +4034,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.79"
|
||||
version = "1.0.81"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95"
|
||||
checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c"
|
||||
dependencies = [
|
||||
"itoa 1.0.1",
|
||||
"ryu",
|
||||
|
@ -4014,6 +4055,28 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_with"
|
||||
version = "1.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "678b5a069e50bf00ecd22d0cd8ddf7c236f68581b03db652061ed5eb13a312ff"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_with_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_with_macros"
|
||||
version = "1.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"proc-macro2 1.0.36",
|
||||
"quote 1.0.14",
|
||||
"syn 1.0.91",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_yaml"
|
||||
version = "0.8.23"
|
||||
|
@ -5985,6 +6048,9 @@ dependencies = [
|
|||
"num-traits",
|
||||
"num_enum",
|
||||
"proptest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_with",
|
||||
"serial_test",
|
||||
"solana-program",
|
||||
"solana-program-test",
|
||||
|
@ -6222,6 +6288,12 @@ version = "0.8.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "strum"
|
||||
version = "0.24.0"
|
||||
|
|
|
@ -11,6 +11,7 @@ exclude = ["js/**"]
|
|||
[features]
|
||||
no-entrypoint = []
|
||||
test-bpf = []
|
||||
serde-traits = ["serde", "serde_with"]
|
||||
# Remove these features once the underlying syscalls are released on all networks
|
||||
default = ["zk-ops"]
|
||||
zk-ops = []
|
||||
|
@ -26,6 +27,8 @@ solana-zk-token-sdk = "1.10.29"
|
|||
spl-memo = { version = "3.0.1", path = "../../memo/program", features = [ "no-entrypoint" ] }
|
||||
spl-token = { version = "3.3", path = "../program", features = ["no-entrypoint"] }
|
||||
thiserror = "1.0"
|
||||
serde = { version = "1.0.136", optional = true }
|
||||
serde_with = { version = "1.14.0", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
lazy_static = "1.4.0"
|
||||
|
@ -33,6 +36,7 @@ proptest = "1.0"
|
|||
serial_test = "0.5.1"
|
||||
solana-program-test = "1.10.29"
|
||||
solana-sdk = "1.10.29"
|
||||
serde_json = "1.0.81"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "lib"]
|
||||
|
|
|
@ -28,6 +28,9 @@ use {
|
|||
},
|
||||
};
|
||||
|
||||
#[cfg(feature = "serde-traits")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Confidential Transfer extension
|
||||
pub mod confidential_transfer;
|
||||
/// Default Account State extension
|
||||
|
@ -601,6 +604,7 @@ impl Default for AccountType {
|
|||
/// applied to mint accounts, and account extensions must only be applied to token holding
|
||||
/// accounts.
|
||||
#[repr(u16)]
|
||||
#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, TryFromPrimitive, IntoPrimitive)]
|
||||
pub enum ExtensionType {
|
||||
/// Used as padding if the account size would otherwise be 355, same as a multisig
|
||||
|
|
|
@ -9,7 +9,14 @@ use {
|
|||
std::convert::TryFrom,
|
||||
};
|
||||
|
||||
#[cfg(feature = "serde-traits")]
|
||||
use {
|
||||
crate::serialization::coption_fromstr,
|
||||
serde::{Deserialize, Serialize},
|
||||
};
|
||||
|
||||
/// Transfer Fee extension instructions
|
||||
#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))]
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
#[repr(u8)]
|
||||
pub enum TransferFeeInstruction {
|
||||
|
@ -27,8 +34,10 @@ pub enum TransferFeeInstruction {
|
|||
/// 0. `[writable]` The mint to initialize.
|
||||
InitializeTransferFeeConfig {
|
||||
/// Pubkey that may update the fees
|
||||
#[cfg_attr(feature = "serde-traits", serde(with = "coption_fromstr"))]
|
||||
transfer_fee_config_authority: COption<Pubkey>,
|
||||
/// Withdraw instructions must be signed by this key
|
||||
#[cfg_attr(feature = "serde-traits", serde(with = "coption_fromstr"))]
|
||||
withdraw_withheld_authority: COption<Pubkey>,
|
||||
/// Amount of transfer collected as fees, expressed as basis points of the
|
||||
/// transfer amount
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
//! Instruction types
|
||||
|
||||
#![allow(deprecated)] // needed to avoid deprecation warning when generating serde implementation for TokenInstruction
|
||||
|
||||
use {
|
||||
crate::{
|
||||
check_program_account, check_spl_token_program_account,
|
||||
|
@ -21,6 +23,13 @@ use {
|
|||
},
|
||||
};
|
||||
|
||||
#[cfg(feature = "serde-traits")]
|
||||
use {
|
||||
crate::serialization::coption_fromstr,
|
||||
serde::{Deserialize, Serialize},
|
||||
serde_with::{As, DisplayFromStr},
|
||||
};
|
||||
|
||||
/// Minimum number of multisignature signers (min N)
|
||||
pub const MIN_SIGNERS: usize = 1;
|
||||
/// Maximum number of multisignature signers (max N)
|
||||
|
@ -32,6 +41,7 @@ const U64_BYTES: usize = 8;
|
|||
|
||||
/// Instructions supported by the token program.
|
||||
#[repr(C)]
|
||||
#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum TokenInstruction<'a> {
|
||||
/// Initializes a new mint and optionally deposits all the newly minted
|
||||
|
@ -54,8 +64,10 @@ pub enum TokenInstruction<'a> {
|
|||
/// Number of base 10 digits to the right of the decimal place.
|
||||
decimals: u8,
|
||||
/// The authority/multisignature to mint tokens.
|
||||
#[cfg_attr(feature = "serde-traits", serde(with = "As::<DisplayFromStr>"))]
|
||||
mint_authority: Pubkey,
|
||||
/// The freeze authority/multisignature of the mint.
|
||||
#[cfg_attr(feature = "serde-traits", serde(with = "coption_fromstr"))]
|
||||
freeze_authority: COption<Pubkey>,
|
||||
},
|
||||
/// Initializes a new account to hold tokens. If this account is associated
|
||||
|
@ -180,6 +192,7 @@ pub enum TokenInstruction<'a> {
|
|||
/// The type of authority to update.
|
||||
authority_type: AuthorityType,
|
||||
/// The new authority
|
||||
#[cfg_attr(feature = "serde-traits", serde(with = "coption_fromstr"))]
|
||||
new_authority: COption<Pubkey>,
|
||||
},
|
||||
/// Mints new tokens to an account. The native mint does not support
|
||||
|
@ -444,8 +457,10 @@ pub enum TokenInstruction<'a> {
|
|||
/// Number of base 10 digits to the right of the decimal place.
|
||||
decimals: u8,
|
||||
/// The authority/multisignature to mint tokens.
|
||||
#[cfg_attr(feature = "serde-traits", serde(with = "As::<DisplayFromStr>"))]
|
||||
mint_authority: Pubkey,
|
||||
/// The freeze authority/multisignature of the mint.
|
||||
#[cfg_attr(feature = "serde-traits", serde(with = "coption_fromstr"))]
|
||||
freeze_authority: COption<Pubkey>,
|
||||
},
|
||||
/// Gets the required size of an account for the given mint as a little-endian
|
||||
|
@ -514,6 +529,7 @@ pub enum TokenInstruction<'a> {
|
|||
/// 0. `[writable]` The mint to initialize.
|
||||
InitializeMintCloseAuthority {
|
||||
/// Authority that must sign the `CloseAccount` instruction on a mint
|
||||
#[cfg_attr(feature = "serde-traits", serde(with = "coption_fromstr"))]
|
||||
close_authority: COption<Pubkey>,
|
||||
},
|
||||
/// The common instruction prefix for Transfer Fee extension instructions.
|
||||
|
@ -933,6 +949,7 @@ impl<'a> TokenInstruction<'a> {
|
|||
|
||||
/// Specifies the authority type for SetAuthority instructions
|
||||
#[repr(u8)]
|
||||
#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum AuthorityType {
|
||||
/// Authority to mint new tokens
|
||||
|
|
|
@ -10,6 +10,8 @@ pub mod instruction;
|
|||
pub mod native_mint;
|
||||
pub mod pod;
|
||||
pub mod processor;
|
||||
#[cfg(feature = "serde-traits")]
|
||||
pub mod serialization;
|
||||
pub mod state;
|
||||
|
||||
#[cfg(not(feature = "no-entrypoint"))]
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
//! serialization module
|
||||
|
||||
/// helper function to ser/deser COption wrapped values
|
||||
pub mod coption_fromstr {
|
||||
use {
|
||||
serde::{
|
||||
de::{Error, Unexpected, Visitor},
|
||||
Deserializer, Serializer,
|
||||
},
|
||||
solana_program::program_option::COption,
|
||||
std::{
|
||||
fmt::{self, Display},
|
||||
marker::PhantomData,
|
||||
str::FromStr,
|
||||
},
|
||||
};
|
||||
|
||||
/// serialize values supporting Display trait wrapped in COption
|
||||
pub fn serialize<S, T>(x: &COption<T>, s: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
T: Display,
|
||||
{
|
||||
match *x {
|
||||
COption::Some(ref value) => s.serialize_some(&value.to_string()),
|
||||
COption::None => s.serialize_none(),
|
||||
}
|
||||
}
|
||||
|
||||
struct COptionVisitor<T> {
|
||||
s: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<'de, T> Visitor<'de> for COptionVisitor<T>
|
||||
where
|
||||
T: FromStr,
|
||||
{
|
||||
type Value = COption<T>;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("a FromStr type")
|
||||
}
|
||||
|
||||
fn visit_some<D>(self, d: D) -> Result<Self::Value, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
d.deserialize_str(self)
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: Error,
|
||||
{
|
||||
T::from_str(v)
|
||||
.map(|r| COption::Some(r))
|
||||
.map_err(|_| E::invalid_value(Unexpected::Str(v), &"value string"))
|
||||
}
|
||||
|
||||
fn visit_none<E>(self) -> Result<Self::Value, E>
|
||||
where
|
||||
E: Error,
|
||||
{
|
||||
Ok(COption::None)
|
||||
}
|
||||
}
|
||||
|
||||
/// deserialize values supporting Display trait wrapped in COption
|
||||
pub fn deserialize<'de, D, T>(d: D) -> Result<COption<T>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
T: FromStr,
|
||||
{
|
||||
d.deserialize_option(COptionVisitor {
|
||||
s: PhantomData::default(),
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
#![cfg(feature = "serde-traits")]
|
||||
|
||||
use {
|
||||
solana_program::program_option::COption, solana_sdk::pubkey::Pubkey,
|
||||
spl_token_2022::instruction, std::str::FromStr,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn token_program_serde() {
|
||||
let inst = instruction::TokenInstruction::InitializeMint2 {
|
||||
decimals: 0,
|
||||
mint_authority: Pubkey::from_str("4uQeVj5tqViQh7yWWGStvkEG1Zmhx6uasJtWCJziofM").unwrap(),
|
||||
freeze_authority: COption::Some(
|
||||
Pubkey::from_str("8opHzTAnfzRpPEx21XtnrVTX28YQuCpAjcn1PczScKh").unwrap(),
|
||||
),
|
||||
};
|
||||
|
||||
let serialized = serde_json::to_string(&inst).unwrap();
|
||||
assert_eq!(&serialized, "{\"InitializeMint2\":{\"decimals\":0,\"mint_authority\":\"4uQeVj5tqViQh7yWWGStvkEG1Zmhx6uasJtWCJziofM\",\"freeze_authority\":\"8opHzTAnfzRpPEx21XtnrVTX28YQuCpAjcn1PczScKh\"}}");
|
||||
|
||||
serde_json::from_str::<instruction::TokenInstruction>(&serialized).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn token_program_serde_with_none() {
|
||||
let inst = instruction::TokenInstruction::InitializeMintCloseAuthority {
|
||||
close_authority: COption::None,
|
||||
};
|
||||
|
||||
let serialized = serde_json::to_string(&inst).unwrap();
|
||||
assert_eq!(
|
||||
&serialized,
|
||||
"{\"InitializeMintCloseAuthority\":{\"close_authority\":null}}"
|
||||
);
|
||||
|
||||
serde_json::from_str::<instruction::TokenInstruction>(&serialized).unwrap();
|
||||
}
|
Loading…
Reference in New Issue