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:
omertxyz 2022-07-07 13:18:47 -04:00 committed by GitHub
parent b5cec79ee4
commit f30ad1d752
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 240 additions and 9 deletions

View File

@ -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:

90
Cargo.lock generated
View File

@ -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"

View File

@ -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"]

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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"))]

View File

@ -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(),
})
}
}

View File

@ -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();
}