v0.4.8-pre.003
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
// file: kb-lib/src/decoder.rs
|
||||
// version: 12
|
||||
// version: 13
|
||||
|
||||
//! Consolidated decoder modules.
|
||||
|
||||
@@ -332,6 +332,14 @@ pub(crate) use self::metadata::DC_METADATA_MTM_VERIFY_COLLECTION_DISCRIMINATOR;
|
||||
pub(crate) use self::metadata::DC_METADATA_MTM_VERIFY_DISCRIMINATOR;
|
||||
/// Verify Sized Collection Item discriminator.
|
||||
pub(crate) use self::metadata::DC_METADATA_MTM_VERIFY_SIZED_COLLECTION_ITEM_DISCRIMINATOR;
|
||||
/// Exact fixed byte length of one Buffer account header.
|
||||
pub use self::metadata::DC_METADATA_SPM_BUFFER_HEADER_BYTES;
|
||||
/// Exact byte length of one external-account data reference.
|
||||
pub use self::metadata::DC_METADATA_SPM_EXTERNAL_DATA_BYTES;
|
||||
/// Exact fixed byte length of one Metadata account header.
|
||||
pub use self::metadata::DC_METADATA_SPM_METADATA_HEADER_BYTES;
|
||||
/// Exact byte length of one Solana Program Metadata seed.
|
||||
pub use self::metadata::DC_METADATA_SPM_SEED_BYTES;
|
||||
/// Exact decoder for the Metaplex Token Metadata program.
|
||||
pub use self::metadata::DcMetadataMetaplexTokenMetadataDecoder;
|
||||
/// Canonical projection shared by Collection and Use Authority records.
|
||||
@@ -384,6 +392,26 @@ pub use self::metadata::DcMetadataMtmTokenRecordAccountSnapshot;
|
||||
pub use self::metadata::DcMetadataSolanaProgramMetadataDecoder;
|
||||
/// Reserved `kb-lib.decoder.metadata.spl_name_service` decoder.
|
||||
pub use self::metadata::DcMetadataSplNameServiceDecoder;
|
||||
/// Solana Program Metadata account discriminator.
|
||||
pub use self::metadata::DcMetadataSpmAccountDiscriminator;
|
||||
/// Compression declared for Solana Program Metadata content.
|
||||
pub use self::metadata::DcMetadataSpmCompression;
|
||||
/// Typed Solana Program Metadata content without off-chain resolution.
|
||||
pub use self::metadata::DcMetadataSpmData;
|
||||
/// On-chain source used for Solana Program Metadata content.
|
||||
pub use self::metadata::DcMetadataSpmDataSource;
|
||||
/// Encoding declared for Solana Program Metadata content.
|
||||
pub use self::metadata::DcMetadataSpmEncoding;
|
||||
/// Fixed external-account data reference.
|
||||
pub use self::metadata::DcMetadataSpmExternalData;
|
||||
/// Format declared for Solana Program Metadata content.
|
||||
pub use self::metadata::DcMetadataSpmFormat;
|
||||
/// Bounded Solana Program Metadata model conversion error.
|
||||
pub use self::metadata::DcMetadataSpmModelError;
|
||||
/// Official Solana Program Metadata custom error.
|
||||
pub use self::metadata::DcMetadataSpmProgramError;
|
||||
/// Exact fixed-size Solana Program Metadata seed.
|
||||
pub use self::metadata::DcMetadataSpmSeed;
|
||||
/// Canonical tracing target for this crate.
|
||||
pub(crate) use self::metadata::TRACING_TARGET_DECODER_METADATA_METAPLEX_TOKEN_METADATA;
|
||||
/// Crate-root access to `decode` from `instruction`.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: kb-lib/src/decoder/metadata.rs
|
||||
// version: 7
|
||||
// version: 8
|
||||
|
||||
//! `metadata` decoder family.
|
||||
|
||||
@@ -77,8 +77,36 @@ pub use self::metaplex_token_metadata::decoder_metadata_metaplex_token_metadata_
|
||||
pub use self::metaplex_token_metadata::decoder_metadata_metaplex_token_metadata_decode_token_record_account;
|
||||
/// Decodes one Use Authority Record account.
|
||||
pub use self::metaplex_token_metadata::decoder_metadata_metaplex_token_metadata_decode_use_authority_record_account;
|
||||
/// Exact fixed byte length of one Buffer account header.
|
||||
pub use self::solana_program_metadata::DC_METADATA_SPM_BUFFER_HEADER_BYTES;
|
||||
/// Exact byte length of one external-account data reference.
|
||||
pub use self::solana_program_metadata::DC_METADATA_SPM_EXTERNAL_DATA_BYTES;
|
||||
/// Exact fixed byte length of one Metadata account header.
|
||||
pub use self::solana_program_metadata::DC_METADATA_SPM_METADATA_HEADER_BYTES;
|
||||
/// Exact byte length of one Solana Program Metadata seed.
|
||||
pub use self::solana_program_metadata::DC_METADATA_SPM_SEED_BYTES;
|
||||
/// Reserved `kb-lib.decoder.metadata.solana_program_metadata` decoder.
|
||||
pub use self::solana_program_metadata::DcMetadataSolanaProgramMetadataDecoder;
|
||||
/// Solana Program Metadata account discriminator.
|
||||
pub use self::solana_program_metadata::DcMetadataSpmAccountDiscriminator;
|
||||
/// Compression declared for Solana Program Metadata content.
|
||||
pub use self::solana_program_metadata::DcMetadataSpmCompression;
|
||||
/// Typed Solana Program Metadata content without off-chain resolution.
|
||||
pub use self::solana_program_metadata::DcMetadataSpmData;
|
||||
/// On-chain source used for Solana Program Metadata content.
|
||||
pub use self::solana_program_metadata::DcMetadataSpmDataSource;
|
||||
/// Encoding declared for Solana Program Metadata content.
|
||||
pub use self::solana_program_metadata::DcMetadataSpmEncoding;
|
||||
/// Fixed external-account data reference.
|
||||
pub use self::solana_program_metadata::DcMetadataSpmExternalData;
|
||||
/// Format declared for Solana Program Metadata content.
|
||||
pub use self::solana_program_metadata::DcMetadataSpmFormat;
|
||||
/// Bounded Solana Program Metadata model conversion error.
|
||||
pub use self::solana_program_metadata::DcMetadataSpmModelError;
|
||||
/// Official Solana Program Metadata custom error.
|
||||
pub use self::solana_program_metadata::DcMetadataSpmProgramError;
|
||||
/// Exact fixed-size Solana Program Metadata seed.
|
||||
pub use self::solana_program_metadata::DcMetadataSpmSeed;
|
||||
/// Reserved `kb-lib.decoder.metadata.spl_name_service` decoder.
|
||||
pub use self::spl_name_service::DcMetadataSplNameServiceDecoder;
|
||||
|
||||
|
||||
@@ -1,7 +1,39 @@
|
||||
// file: kb-lib/src/decoder/metadata/solana_program_metadata.rs
|
||||
// version: 1
|
||||
// version: 2
|
||||
|
||||
//! Reserved protocol decoder for `metadata_solana_program_metadata`.
|
||||
//! Public Solana Program Metadata models and reserved protocol decoder.
|
||||
|
||||
mod constants;
|
||||
mod model;
|
||||
|
||||
/// Exact fixed byte length of one Buffer account header.
|
||||
pub use self::constants::DC_METADATA_SPM_BUFFER_HEADER_BYTES;
|
||||
/// Exact byte length of one external-account data reference.
|
||||
pub use self::constants::DC_METADATA_SPM_EXTERNAL_DATA_BYTES;
|
||||
/// Exact fixed byte length of one Metadata account header.
|
||||
pub use self::constants::DC_METADATA_SPM_METADATA_HEADER_BYTES;
|
||||
/// Exact byte length of one Solana Program Metadata seed.
|
||||
pub use self::constants::DC_METADATA_SPM_SEED_BYTES;
|
||||
/// Solana Program Metadata account discriminator.
|
||||
pub use self::model::DcMetadataSpmAccountDiscriminator;
|
||||
/// Compression declared for metadata content.
|
||||
pub use self::model::DcMetadataSpmCompression;
|
||||
/// Typed on-chain metadata content without off-chain resolution.
|
||||
pub use self::model::DcMetadataSpmData;
|
||||
/// On-chain source used for metadata content.
|
||||
pub use self::model::DcMetadataSpmDataSource;
|
||||
/// Encoding declared for metadata content.
|
||||
pub use self::model::DcMetadataSpmEncoding;
|
||||
/// Fixed external-account data reference.
|
||||
pub use self::model::DcMetadataSpmExternalData;
|
||||
/// Format declared for metadata content.
|
||||
pub use self::model::DcMetadataSpmFormat;
|
||||
/// Bounded model conversion error.
|
||||
pub use self::model::DcMetadataSpmModelError;
|
||||
/// Official Solana Program Metadata custom error.
|
||||
pub use self::model::DcMetadataSpmProgramError;
|
||||
/// Exact fixed-size metadata seed.
|
||||
pub use self::model::DcMetadataSpmSeed;
|
||||
|
||||
/// Reserved decoder for the `metadata_solana_program_metadata` program surface.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
// file: kb-lib/src/decoder/metadata/solana_program_metadata/constants.rs
|
||||
// version: 1
|
||||
|
||||
//! Bounded Solana Program Metadata wire constants.
|
||||
|
||||
/// Exact byte length of one Solana Program Metadata seed.
|
||||
pub const DC_METADATA_SPM_SEED_BYTES: usize = 16;
|
||||
/// Exact fixed byte length of one Buffer account header.
|
||||
pub const DC_METADATA_SPM_BUFFER_HEADER_BYTES: usize = 96;
|
||||
/// Exact fixed byte length of one Metadata account header.
|
||||
pub const DC_METADATA_SPM_METADATA_HEADER_BYTES: usize = 96;
|
||||
/// Exact byte length of one external-account data reference.
|
||||
pub const DC_METADATA_SPM_EXTERNAL_DATA_BYTES: usize = 40;
|
||||
740
kb-lib/src/decoder/metadata/solana_program_metadata/model.rs
Normal file
740
kb-lib/src/decoder/metadata/solana_program_metadata/model.rs
Normal file
@@ -0,0 +1,740 @@
|
||||
// file: kb-lib/src/decoder/metadata/solana_program_metadata/model.rs
|
||||
// version: 1
|
||||
|
||||
//! Public bounded models for the Solana Program Metadata wire contract.
|
||||
|
||||
/// Solana Program Metadata account discriminator.
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, serde::Deserialize, serde::Serialize)]
|
||||
#[repr(u8)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum DcMetadataSpmAccountDiscriminator {
|
||||
/// Uninitialized account data.
|
||||
Empty = 0,
|
||||
/// Writable staging buffer.
|
||||
Buffer = 1,
|
||||
/// Initialized program metadata account.
|
||||
Metadata = 2,
|
||||
}
|
||||
|
||||
impl DcMetadataSpmAccountDiscriminator {
|
||||
/// Returns the exact wire discriminant.
|
||||
pub const fn wire_value(self) -> u8 {
|
||||
return self as u8;
|
||||
}
|
||||
|
||||
/// Resolves an exact wire discriminant.
|
||||
pub fn from_wire_value(value: u8) -> std::result::Result<Self, crate::DcMetadataSpmModelError> {
|
||||
match value {
|
||||
0 => return std::result::Result::Ok(Self::Empty),
|
||||
1 => return std::result::Result::Ok(Self::Buffer),
|
||||
2 => return std::result::Result::Ok(Self::Metadata),
|
||||
_ => {
|
||||
return std::result::Result::Err(
|
||||
crate::DcMetadataSpmModelError::UnknownAccountDiscriminator { value },
|
||||
);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Encoding declared for metadata content.
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, serde::Deserialize, serde::Serialize)]
|
||||
#[repr(u8)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum DcMetadataSpmEncoding {
|
||||
/// No content encoding is declared.
|
||||
None = 0,
|
||||
/// UTF-8 content encoding.
|
||||
Utf8 = 1,
|
||||
/// Base58 content encoding.
|
||||
Base58 = 2,
|
||||
/// Base64 content encoding.
|
||||
Base64 = 3,
|
||||
}
|
||||
|
||||
impl DcMetadataSpmEncoding {
|
||||
/// Returns the exact wire discriminant.
|
||||
pub const fn wire_value(self) -> u8 {
|
||||
return self as u8;
|
||||
}
|
||||
|
||||
/// Resolves an exact wire discriminant.
|
||||
pub fn from_wire_value(value: u8) -> std::result::Result<Self, crate::DcMetadataSpmModelError> {
|
||||
match value {
|
||||
0 => return std::result::Result::Ok(Self::None),
|
||||
1 => return std::result::Result::Ok(Self::Utf8),
|
||||
2 => return std::result::Result::Ok(Self::Base58),
|
||||
3 => return std::result::Result::Ok(Self::Base64),
|
||||
_ => {
|
||||
return std::result::Result::Err(crate::DcMetadataSpmModelError::UnknownEncoding {
|
||||
value,
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Compression declared for metadata content.
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, serde::Deserialize, serde::Serialize)]
|
||||
#[repr(u8)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum DcMetadataSpmCompression {
|
||||
/// No content compression is declared.
|
||||
None = 0,
|
||||
/// Gzip content compression.
|
||||
Gzip = 1,
|
||||
/// Zlib content compression.
|
||||
Zlib = 2,
|
||||
}
|
||||
|
||||
impl DcMetadataSpmCompression {
|
||||
/// Returns the exact wire discriminant.
|
||||
pub const fn wire_value(self) -> u8 {
|
||||
return self as u8;
|
||||
}
|
||||
|
||||
/// Resolves an exact wire discriminant.
|
||||
pub fn from_wire_value(value: u8) -> std::result::Result<Self, crate::DcMetadataSpmModelError> {
|
||||
match value {
|
||||
0 => return std::result::Result::Ok(Self::None),
|
||||
1 => return std::result::Result::Ok(Self::Gzip),
|
||||
2 => return std::result::Result::Ok(Self::Zlib),
|
||||
_ => {
|
||||
return std::result::Result::Err(
|
||||
crate::DcMetadataSpmModelError::UnknownCompression { value },
|
||||
);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Format declared for metadata content.
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, serde::Deserialize, serde::Serialize)]
|
||||
#[repr(u8)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum DcMetadataSpmFormat {
|
||||
/// No structured content format is declared.
|
||||
None = 0,
|
||||
/// JSON content format.
|
||||
Json = 1,
|
||||
/// YAML content format.
|
||||
Yaml = 2,
|
||||
/// TOML content format.
|
||||
Toml = 3,
|
||||
}
|
||||
|
||||
impl DcMetadataSpmFormat {
|
||||
/// Returns the exact wire discriminant.
|
||||
pub const fn wire_value(self) -> u8 {
|
||||
return self as u8;
|
||||
}
|
||||
|
||||
/// Resolves an exact wire discriminant.
|
||||
pub fn from_wire_value(value: u8) -> std::result::Result<Self, crate::DcMetadataSpmModelError> {
|
||||
match value {
|
||||
0 => return std::result::Result::Ok(Self::None),
|
||||
1 => return std::result::Result::Ok(Self::Json),
|
||||
2 => return std::result::Result::Ok(Self::Yaml),
|
||||
3 => return std::result::Result::Ok(Self::Toml),
|
||||
_ => {
|
||||
return std::result::Result::Err(crate::DcMetadataSpmModelError::UnknownFormat {
|
||||
value,
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// On-chain source used for metadata content.
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, serde::Deserialize, serde::Serialize)]
|
||||
#[repr(u8)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum DcMetadataSpmDataSource {
|
||||
/// Content bytes are stored directly after the account header.
|
||||
Direct = 0,
|
||||
/// A UTF-8 URL is stored after the account header.
|
||||
Url = 1,
|
||||
/// A fixed external-account reference is stored after the account header.
|
||||
External = 2,
|
||||
}
|
||||
|
||||
impl DcMetadataSpmDataSource {
|
||||
/// Returns the exact wire discriminant.
|
||||
pub const fn wire_value(self) -> u8 {
|
||||
return self as u8;
|
||||
}
|
||||
|
||||
/// Resolves an exact wire discriminant.
|
||||
pub fn from_wire_value(value: u8) -> std::result::Result<Self, crate::DcMetadataSpmModelError> {
|
||||
match value {
|
||||
0 => return std::result::Result::Ok(Self::Direct),
|
||||
1 => return std::result::Result::Ok(Self::Url),
|
||||
2 => return std::result::Result::Ok(Self::External),
|
||||
_ => {
|
||||
return std::result::Result::Err(
|
||||
crate::DcMetadataSpmModelError::UnknownDataSource { value },
|
||||
);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Official custom error returned by the Solana Program Metadata program.
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, serde::Deserialize, serde::Serialize)]
|
||||
#[repr(u32)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum DcMetadataSpmProgramError {
|
||||
/// The referenced program account is not executable.
|
||||
NotExecutableAccount = 0,
|
||||
/// The referenced upgradeable-program state is invalid.
|
||||
InvalidProgramState = 1,
|
||||
/// The referenced program-data account is invalid.
|
||||
InvalidProgramDataAccount = 2,
|
||||
/// The metadata account is immutable.
|
||||
ImmutableMetadataAccount = 3,
|
||||
/// The metadata data length is invalid for its source.
|
||||
InvalidDataLength = 4,
|
||||
}
|
||||
|
||||
impl DcMetadataSpmProgramError {
|
||||
/// Returns the exact custom program error code.
|
||||
pub const fn code(self) -> u32 {
|
||||
return self as u32;
|
||||
}
|
||||
|
||||
/// Resolves an exact custom program error code.
|
||||
pub fn from_code(value: u32) -> std::result::Result<Self, crate::DcMetadataSpmModelError> {
|
||||
match value {
|
||||
0 => return std::result::Result::Ok(Self::NotExecutableAccount),
|
||||
1 => return std::result::Result::Ok(Self::InvalidProgramState),
|
||||
2 => return std::result::Result::Ok(Self::InvalidProgramDataAccount),
|
||||
3 => return std::result::Result::Ok(Self::ImmutableMetadataAccount),
|
||||
4 => return std::result::Result::Ok(Self::InvalidDataLength),
|
||||
_ => {
|
||||
return std::result::Result::Err(
|
||||
crate::DcMetadataSpmModelError::UnknownProgramError { value },
|
||||
);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Exact fixed-size seed carried by PDA derivations and account headers.
|
||||
#[derive(
|
||||
Clone, Copy, Debug, Default, Eq, Hash, PartialEq, serde::Deserialize, serde::Serialize,
|
||||
)]
|
||||
#[serde(transparent)]
|
||||
pub struct DcMetadataSpmSeed([u8; crate::DC_METADATA_SPM_SEED_BYTES]);
|
||||
|
||||
impl DcMetadataSpmSeed {
|
||||
/// Creates a seed from its exact fixed-size wire representation.
|
||||
pub const fn from_bytes(bytes: [u8; crate::DC_METADATA_SPM_SEED_BYTES]) -> Self {
|
||||
return Self(bytes);
|
||||
}
|
||||
|
||||
/// Creates a zero-padded fixed-size seed from one UTF-8 value.
|
||||
pub fn from_utf8(value: &str) -> std::result::Result<Self, crate::DcMetadataSpmModelError> {
|
||||
let source = value.as_bytes();
|
||||
if source.len() > crate::DC_METADATA_SPM_SEED_BYTES {
|
||||
return std::result::Result::Err(crate::DcMetadataSpmModelError::SeedUtf8TooLong {
|
||||
actual_bytes: source.len(),
|
||||
max_bytes: crate::DC_METADATA_SPM_SEED_BYTES,
|
||||
});
|
||||
}
|
||||
let mut bytes = [0_u8; crate::DC_METADATA_SPM_SEED_BYTES];
|
||||
bytes[..source.len()].copy_from_slice(source);
|
||||
return std::result::Result::Ok(Self(bytes));
|
||||
}
|
||||
|
||||
/// Parses an exact fixed-size wire slice.
|
||||
pub fn from_wire_bytes(
|
||||
bytes: &[u8],
|
||||
) -> std::result::Result<Self, crate::DcMetadataSpmModelError> {
|
||||
if bytes.len() != crate::DC_METADATA_SPM_SEED_BYTES {
|
||||
return std::result::Result::Err(crate::DcMetadataSpmModelError::InvalidSeedLength {
|
||||
actual_bytes: bytes.len(),
|
||||
expected_bytes: crate::DC_METADATA_SPM_SEED_BYTES,
|
||||
});
|
||||
}
|
||||
let mut seed = [0_u8; crate::DC_METADATA_SPM_SEED_BYTES];
|
||||
seed.copy_from_slice(bytes);
|
||||
return std::result::Result::Ok(Self(seed));
|
||||
}
|
||||
|
||||
/// Returns the exact fixed-size wire representation.
|
||||
pub const fn as_bytes(&self) -> &[u8; crate::DC_METADATA_SPM_SEED_BYTES] {
|
||||
return &self.0;
|
||||
}
|
||||
|
||||
/// Returns the UTF-8 value after removing only terminal zero padding.
|
||||
pub fn trimmed_utf8(&self) -> std::result::Result<&str, crate::DcMetadataSpmModelError> {
|
||||
let mut length = self.0.len();
|
||||
while length > 0 && self.0[length - 1] == 0 {
|
||||
length -= 1;
|
||||
}
|
||||
match std::str::from_utf8(&self.0[..length]) {
|
||||
std::result::Result::Ok(value) => return std::result::Result::Ok(value),
|
||||
std::result::Result::Err(_) => {
|
||||
return std::result::Result::Err(crate::DcMetadataSpmModelError::InvalidSeedUtf8);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Fixed external-account data reference used by the `External` data source.
|
||||
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
|
||||
pub struct DcMetadataSpmExternalData {
|
||||
/// Account that stores the referenced data.
|
||||
pub address: solana_pubkey::Pubkey,
|
||||
/// Byte offset inside the external account.
|
||||
pub offset: u32,
|
||||
/// Optional non-zero byte length; `None` is encoded as zero and means the whole account.
|
||||
pub length: std::option::Option<std::num::NonZeroU32>,
|
||||
}
|
||||
|
||||
impl DcMetadataSpmExternalData {
|
||||
/// Creates an exact external-account reference.
|
||||
pub const fn new(
|
||||
address: solana_pubkey::Pubkey,
|
||||
offset: u32,
|
||||
length: std::option::Option<std::num::NonZeroU32>,
|
||||
) -> Self {
|
||||
return Self { address, offset, length };
|
||||
}
|
||||
|
||||
/// Creates an external-account reference from a zeroable optional length.
|
||||
pub fn from_zeroable_length(address: solana_pubkey::Pubkey, offset: u32, length: u32) -> Self {
|
||||
return Self::new(address, offset, std::num::NonZeroU32::new(length));
|
||||
}
|
||||
|
||||
/// Serializes the exact 40-byte little-endian wire representation.
|
||||
pub fn to_wire_bytes(&self) -> [u8; crate::DC_METADATA_SPM_EXTERNAL_DATA_BYTES] {
|
||||
let mut bytes = [0_u8; crate::DC_METADATA_SPM_EXTERNAL_DATA_BYTES];
|
||||
bytes[..32].copy_from_slice(&self.address.to_bytes());
|
||||
bytes[32..36].copy_from_slice(&self.offset.to_le_bytes());
|
||||
let length = match self.length {
|
||||
std::option::Option::Some(value) => value.get(),
|
||||
std::option::Option::None => 0,
|
||||
};
|
||||
bytes[36..40].copy_from_slice(&length.to_le_bytes());
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/// Parses the exact 40-byte little-endian wire representation.
|
||||
pub fn from_wire_bytes(
|
||||
bytes: &[u8],
|
||||
) -> std::result::Result<Self, crate::DcMetadataSpmModelError> {
|
||||
if bytes.len() != crate::DC_METADATA_SPM_EXTERNAL_DATA_BYTES {
|
||||
return std::result::Result::Err(
|
||||
crate::DcMetadataSpmModelError::InvalidExternalDataLength {
|
||||
actual_bytes: bytes.len(),
|
||||
expected_bytes: crate::DC_METADATA_SPM_EXTERNAL_DATA_BYTES,
|
||||
},
|
||||
);
|
||||
}
|
||||
let mut address = [0_u8; 32];
|
||||
address.copy_from_slice(&bytes[..32]);
|
||||
let mut offset = [0_u8; 4];
|
||||
offset.copy_from_slice(&bytes[32..36]);
|
||||
let mut length = [0_u8; 4];
|
||||
length.copy_from_slice(&bytes[36..40]);
|
||||
return std::result::Result::Ok(Self::from_zeroable_length(
|
||||
solana_pubkey::Pubkey::new_from_array(address),
|
||||
u32::from_le_bytes(offset),
|
||||
u32::from_le_bytes(length),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// Typed on-chain metadata content without off-chain resolution.
|
||||
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(tag = "source", content = "value", rename_all = "snake_case")]
|
||||
pub enum DcMetadataSpmData {
|
||||
/// Raw content stored directly in the metadata account.
|
||||
Direct(std::vec::Vec<u8>),
|
||||
/// UTF-8 URL stored directly in the metadata account.
|
||||
Url(String),
|
||||
/// Fixed reference to data stored in another account.
|
||||
External(crate::DcMetadataSpmExternalData),
|
||||
}
|
||||
|
||||
impl DcMetadataSpmData {
|
||||
/// Returns the source discriminator associated with this content.
|
||||
pub const fn source(&self) -> crate::DcMetadataSpmDataSource {
|
||||
match self {
|
||||
Self::Direct(_) => return crate::DcMetadataSpmDataSource::Direct,
|
||||
Self::Url(_) => return crate::DcMetadataSpmDataSource::Url,
|
||||
Self::External(_) => return crate::DcMetadataSpmDataSource::External,
|
||||
}
|
||||
}
|
||||
|
||||
/// Validates source-specific length requirements and the `u32` header bound.
|
||||
pub fn validate(&self) -> std::result::Result<(), crate::DcMetadataSpmModelError> {
|
||||
let actual_bytes = self.wire_byte_len();
|
||||
match self {
|
||||
Self::Direct(_) | Self::Url(_) if actual_bytes == 0 => {
|
||||
return std::result::Result::Err(crate::DcMetadataSpmModelError::EmptyData {
|
||||
source: self.source(),
|
||||
});
|
||||
},
|
||||
Self::Direct(_) | Self::Url(_) | Self::External(_) => {},
|
||||
}
|
||||
if u32::try_from(actual_bytes).is_err() {
|
||||
return std::result::Result::Err(
|
||||
crate::DcMetadataSpmModelError::DataLengthExceedsU32 { actual_bytes },
|
||||
);
|
||||
}
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
|
||||
/// Returns the exact number of bytes represented on wire.
|
||||
pub fn wire_byte_len(&self) -> usize {
|
||||
match self {
|
||||
Self::Direct(value) => return value.len(),
|
||||
Self::Url(value) => return value.len(),
|
||||
Self::External(_) => return crate::DC_METADATA_SPM_EXTERNAL_DATA_BYTES,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the exact little-endian header length after validation.
|
||||
pub fn wire_length_u32(&self) -> std::result::Result<u32, crate::DcMetadataSpmModelError> {
|
||||
match self.validate() {
|
||||
std::result::Result::Ok(()) => {},
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
let actual_bytes = self.wire_byte_len();
|
||||
match u32::try_from(actual_bytes) {
|
||||
std::result::Result::Ok(value) => return std::result::Result::Ok(value),
|
||||
std::result::Result::Err(_) => {
|
||||
return std::result::Result::Err(
|
||||
crate::DcMetadataSpmModelError::DataLengthExceedsU32 { actual_bytes },
|
||||
);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Serializes the exact content bytes without fetching URL or external-account data.
|
||||
pub fn to_wire_bytes(
|
||||
&self,
|
||||
) -> std::result::Result<std::vec::Vec<u8>, crate::DcMetadataSpmModelError> {
|
||||
match self.validate() {
|
||||
std::result::Result::Ok(()) => {},
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
match self {
|
||||
Self::Direct(value) => return std::result::Result::Ok(value.clone()),
|
||||
Self::Url(value) => {
|
||||
return std::result::Result::Ok(value.as_bytes().to_vec());
|
||||
},
|
||||
Self::External(value) => {
|
||||
return std::result::Result::Ok(value.to_wire_bytes().to_vec());
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Bounded error for Solana Program Metadata public model conversions.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub enum DcMetadataSpmModelError {
|
||||
/// An account discriminator is outside the published closed enum.
|
||||
UnknownAccountDiscriminator {
|
||||
/// Rejected wire value.
|
||||
value: u8,
|
||||
},
|
||||
/// An encoding discriminator is outside the published closed enum.
|
||||
UnknownEncoding {
|
||||
/// Rejected wire value.
|
||||
value: u8,
|
||||
},
|
||||
/// A compression discriminator is outside the published closed enum.
|
||||
UnknownCompression {
|
||||
/// Rejected wire value.
|
||||
value: u8,
|
||||
},
|
||||
/// A format discriminator is outside the published closed enum.
|
||||
UnknownFormat {
|
||||
/// Rejected wire value.
|
||||
value: u8,
|
||||
},
|
||||
/// A data-source discriminator is outside the published closed enum.
|
||||
UnknownDataSource {
|
||||
/// Rejected wire value.
|
||||
value: u8,
|
||||
},
|
||||
/// A custom program error code is outside the published closed enum.
|
||||
UnknownProgramError {
|
||||
/// Rejected custom error code.
|
||||
value: u32,
|
||||
},
|
||||
/// One UTF-8 seed exceeds the exact fixed-size wire bound.
|
||||
SeedUtf8TooLong {
|
||||
/// Actual UTF-8 byte length.
|
||||
actual_bytes: usize,
|
||||
/// Maximum accepted UTF-8 byte length.
|
||||
max_bytes: usize,
|
||||
},
|
||||
/// One seed slice does not have the exact fixed-size wire length.
|
||||
InvalidSeedLength {
|
||||
/// Actual slice length.
|
||||
actual_bytes: usize,
|
||||
/// Required fixed length.
|
||||
expected_bytes: usize,
|
||||
},
|
||||
/// The non-padding seed bytes are not valid UTF-8.
|
||||
InvalidSeedUtf8,
|
||||
/// One external-data slice does not have the exact fixed-size wire length.
|
||||
InvalidExternalDataLength {
|
||||
/// Actual slice length.
|
||||
actual_bytes: usize,
|
||||
/// Required fixed length.
|
||||
expected_bytes: usize,
|
||||
},
|
||||
/// Direct or URL content is empty.
|
||||
EmptyData {
|
||||
/// Source whose data is empty.
|
||||
source: crate::DcMetadataSpmDataSource,
|
||||
},
|
||||
/// Content cannot be represented by the `u32` length stored in the account header.
|
||||
DataLengthExceedsU32 {
|
||||
/// Actual content byte length.
|
||||
actual_bytes: usize,
|
||||
},
|
||||
}
|
||||
|
||||
impl std::fmt::Display for DcMetadataSpmModelError {
|
||||
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::UnknownAccountDiscriminator { value } => {
|
||||
return write!(formatter, "unknown account discriminator {value}");
|
||||
},
|
||||
Self::UnknownEncoding { value } => {
|
||||
return write!(formatter, "unknown encoding discriminator {value}");
|
||||
},
|
||||
Self::UnknownCompression { value } => {
|
||||
return write!(formatter, "unknown compression discriminator {value}");
|
||||
},
|
||||
Self::UnknownFormat { value } => {
|
||||
return write!(formatter, "unknown format discriminator {value}");
|
||||
},
|
||||
Self::UnknownDataSource { value } => {
|
||||
return write!(formatter, "unknown data-source discriminator {value}");
|
||||
},
|
||||
Self::UnknownProgramError { value } => {
|
||||
return write!(formatter, "unknown program metadata error code {value}");
|
||||
},
|
||||
Self::SeedUtf8TooLong { actual_bytes, max_bytes } => {
|
||||
return write!(
|
||||
formatter,
|
||||
"seed UTF-8 length {actual_bytes} exceeds {max_bytes} bytes",
|
||||
);
|
||||
},
|
||||
Self::InvalidSeedLength { actual_bytes, expected_bytes } => {
|
||||
return write!(
|
||||
formatter,
|
||||
"seed length {actual_bytes} does not equal {expected_bytes} bytes",
|
||||
);
|
||||
},
|
||||
Self::InvalidSeedUtf8 => {
|
||||
return formatter.write_str("seed contains invalid UTF-8 before terminal padding");
|
||||
},
|
||||
Self::InvalidExternalDataLength { actual_bytes, expected_bytes } => {
|
||||
return write!(
|
||||
formatter,
|
||||
"external data length {actual_bytes} does not equal {expected_bytes} bytes",
|
||||
);
|
||||
},
|
||||
Self::EmptyData { source } => {
|
||||
return write!(formatter, "metadata data source {source:?} cannot be empty");
|
||||
},
|
||||
Self::DataLengthExceedsU32 { actual_bytes } => {
|
||||
return write!(
|
||||
formatter,
|
||||
"metadata data length {actual_bytes} exceeds the u32 wire bound",
|
||||
);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for DcMetadataSpmModelError {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn published_wire_values_are_exact_and_closed() {
|
||||
assert_eq!(crate::DcMetadataSpmAccountDiscriminator::Empty.wire_value(), 0);
|
||||
assert_eq!(crate::DcMetadataSpmAccountDiscriminator::Buffer.wire_value(), 1);
|
||||
assert_eq!(crate::DcMetadataSpmAccountDiscriminator::Metadata.wire_value(), 2);
|
||||
assert_eq!(crate::DcMetadataSpmEncoding::None.wire_value(), 0);
|
||||
assert_eq!(crate::DcMetadataSpmEncoding::Utf8.wire_value(), 1);
|
||||
assert_eq!(crate::DcMetadataSpmEncoding::Base58.wire_value(), 2);
|
||||
assert_eq!(crate::DcMetadataSpmEncoding::Base64.wire_value(), 3);
|
||||
assert_eq!(crate::DcMetadataSpmCompression::None.wire_value(), 0);
|
||||
assert_eq!(crate::DcMetadataSpmCompression::Gzip.wire_value(), 1);
|
||||
assert_eq!(crate::DcMetadataSpmCompression::Zlib.wire_value(), 2);
|
||||
assert_eq!(crate::DcMetadataSpmFormat::None.wire_value(), 0);
|
||||
assert_eq!(crate::DcMetadataSpmFormat::Json.wire_value(), 1);
|
||||
assert_eq!(crate::DcMetadataSpmFormat::Yaml.wire_value(), 2);
|
||||
assert_eq!(crate::DcMetadataSpmFormat::Toml.wire_value(), 3);
|
||||
assert_eq!(crate::DcMetadataSpmDataSource::Direct.wire_value(), 0);
|
||||
assert_eq!(crate::DcMetadataSpmDataSource::Url.wire_value(), 1);
|
||||
assert_eq!(crate::DcMetadataSpmDataSource::External.wire_value(), 2);
|
||||
assert_eq!(crate::DcMetadataSpmProgramError::NotExecutableAccount.code(), 0);
|
||||
assert_eq!(crate::DcMetadataSpmProgramError::InvalidProgramState.code(), 1);
|
||||
assert_eq!(crate::DcMetadataSpmProgramError::InvalidProgramDataAccount.code(), 2);
|
||||
assert_eq!(crate::DcMetadataSpmProgramError::ImmutableMetadataAccount.code(), 3);
|
||||
assert_eq!(crate::DcMetadataSpmProgramError::InvalidDataLength.code(), 4);
|
||||
assert!(crate::DcMetadataSpmAccountDiscriminator::from_wire_value(3).is_err());
|
||||
assert!(crate::DcMetadataSpmEncoding::from_wire_value(4).is_err());
|
||||
assert!(crate::DcMetadataSpmCompression::from_wire_value(3).is_err());
|
||||
assert!(crate::DcMetadataSpmFormat::from_wire_value(4).is_err());
|
||||
assert!(crate::DcMetadataSpmDataSource::from_wire_value(3).is_err());
|
||||
assert!(crate::DcMetadataSpmProgramError::from_code(5).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn seed_preserves_exact_bytes_and_applies_the_utf8_byte_bound() {
|
||||
let seed_result = crate::DcMetadataSpmSeed::from_utf8("sécurité");
|
||||
assert!(seed_result.is_ok());
|
||||
let seed;
|
||||
if let std::result::Result::Ok(value) = seed_result {
|
||||
seed = value;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
assert_eq!(seed.as_bytes().len(), crate::DC_METADATA_SPM_SEED_BYTES);
|
||||
assert_eq!(seed.trimmed_utf8(), std::result::Result::Ok("sécurité"));
|
||||
assert!(crate::DcMetadataSpmSeed::from_utf8("éééééééé").is_ok());
|
||||
assert_eq!(
|
||||
crate::DcMetadataSpmSeed::from_utf8("ééééééééé"),
|
||||
std::result::Result::Err(crate::DcMetadataSpmModelError::SeedUtf8TooLong {
|
||||
actual_bytes: 18,
|
||||
max_bytes: crate::DC_METADATA_SPM_SEED_BYTES,
|
||||
}),
|
||||
);
|
||||
assert!(crate::DcMetadataSpmSeed::from_wire_bytes(&[0_u8; 15]).is_err());
|
||||
let invalid = crate::DcMetadataSpmSeed::from_bytes([
|
||||
0xff, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8,
|
||||
0_u8, 0_u8,
|
||||
]);
|
||||
assert_eq!(
|
||||
invalid.trimmed_utf8(),
|
||||
std::result::Result::Err(crate::DcMetadataSpmModelError::InvalidSeedUtf8),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn external_data_round_trips_the_exact_zeroable_layout() {
|
||||
let address = solana_pubkey::Pubkey::new_from_array([7_u8; 32]);
|
||||
let length_option = std::num::NonZeroU32::new(9);
|
||||
assert!(length_option.is_some());
|
||||
let length;
|
||||
if let std::option::Option::Some(value) = length_option {
|
||||
length = value;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
let model = crate::DcMetadataSpmExternalData::new(
|
||||
address,
|
||||
0x1122_3344,
|
||||
std::option::Option::Some(length),
|
||||
);
|
||||
let bytes = model.to_wire_bytes();
|
||||
assert_eq!(bytes.len(), crate::DC_METADATA_SPM_EXTERNAL_DATA_BYTES);
|
||||
assert_eq!(&bytes[..32], &[7_u8; 32]);
|
||||
assert_eq!(&bytes[32..36], &0x1122_3344_u32.to_le_bytes());
|
||||
assert_eq!(&bytes[36..40], &9_u32.to_le_bytes());
|
||||
assert_eq!(
|
||||
crate::DcMetadataSpmExternalData::from_wire_bytes(&bytes),
|
||||
std::result::Result::Ok(model),
|
||||
);
|
||||
let zeroable = crate::DcMetadataSpmExternalData::from_zeroable_length(address, 3, 0);
|
||||
assert_eq!(zeroable.length, std::option::Option::None);
|
||||
assert_eq!(&zeroable.to_wire_bytes()[36..40], &0_u32.to_le_bytes());
|
||||
assert!(crate::DcMetadataSpmExternalData::from_wire_bytes(&bytes[..39]).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn typed_data_keeps_url_and_external_content_on_chain_only() {
|
||||
let direct = crate::DcMetadataSpmData::Direct(vec![1_u8, 2_u8, 3_u8]);
|
||||
assert_eq!(direct.source(), crate::DcMetadataSpmDataSource::Direct);
|
||||
assert_eq!(direct.wire_length_u32(), std::result::Result::Ok(3));
|
||||
assert_eq!(direct.to_wire_bytes(), std::result::Result::Ok(vec![1_u8, 2_u8, 3_u8]));
|
||||
let url = crate::DcMetadataSpmData::Url("https://example.invalid/idl.json".to_string());
|
||||
assert_eq!(url.source(), crate::DcMetadataSpmDataSource::Url);
|
||||
assert_eq!(
|
||||
url.to_wire_bytes(),
|
||||
std::result::Result::Ok(b"https://example.invalid/idl.json".to_vec()),
|
||||
);
|
||||
let external = crate::DcMetadataSpmData::External(
|
||||
crate::DcMetadataSpmExternalData::from_zeroable_length(
|
||||
solana_pubkey::Pubkey::new_from_array([8_u8; 32]),
|
||||
12,
|
||||
20,
|
||||
),
|
||||
);
|
||||
assert_eq!(external.source(), crate::DcMetadataSpmDataSource::External);
|
||||
assert_eq!(external.wire_length_u32(), std::result::Result::Ok(40),);
|
||||
assert_eq!(
|
||||
external.to_wire_bytes().map(|value| return value.len()),
|
||||
std::result::Result::Ok(crate::DC_METADATA_SPM_EXTERNAL_DATA_BYTES),
|
||||
);
|
||||
assert!(crate::DcMetadataSpmData::Direct(std::vec::Vec::new()).validate().is_err());
|
||||
assert!(crate::DcMetadataSpmData::Url(String::new()).validate().is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn public_json_representation_is_stable_and_typed() {
|
||||
let encoding_result = serde_json::to_value(crate::DcMetadataSpmEncoding::Base64);
|
||||
assert!(encoding_result.is_ok());
|
||||
let encoding;
|
||||
if let std::result::Result::Ok(value) = encoding_result {
|
||||
encoding = value;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
assert_eq!(encoding, serde_json::json!("base64"));
|
||||
let url_result = serde_json::to_value(crate::DcMetadataSpmData::Url(
|
||||
"https://example.invalid/metadata.json".to_string(),
|
||||
));
|
||||
assert!(url_result.is_ok());
|
||||
let url;
|
||||
if let std::result::Result::Ok(value) = url_result {
|
||||
url = value;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
assert_eq!(
|
||||
url,
|
||||
serde_json::json!({
|
||||
"source": "url",
|
||||
"value": "https://example.invalid/metadata.json",
|
||||
}),
|
||||
);
|
||||
let seed_result = crate::DcMetadataSpmSeed::from_utf8("idl");
|
||||
assert!(seed_result.is_ok());
|
||||
let seed;
|
||||
if let std::result::Result::Ok(value) = seed_result {
|
||||
seed = value;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
let json_result = serde_json::to_value(seed);
|
||||
assert!(json_result.is_ok());
|
||||
let json;
|
||||
if let std::result::Result::Ok(value) = json_result {
|
||||
json = value;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
assert_eq!(json.as_array().map(std::vec::Vec::len), std::option::Option::Some(16));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn published_fixed_sizes_match_the_audited_contract() {
|
||||
assert_eq!(crate::DC_METADATA_SPM_SEED_BYTES, 16);
|
||||
assert_eq!(crate::DC_METADATA_SPM_BUFFER_HEADER_BYTES, 96);
|
||||
assert_eq!(crate::DC_METADATA_SPM_METADATA_HEADER_BYTES, 96);
|
||||
assert_eq!(crate::DC_METADATA_SPM_EXTERNAL_DATA_BYTES, 40);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: kb-lib/src/lib.rs
|
||||
// version: 29
|
||||
// version: 30
|
||||
|
||||
//! Consolidated decoder, executor, materializer and shared model library.
|
||||
#![warn(missing_docs)]
|
||||
@@ -15,6 +15,14 @@ mod model;
|
||||
pub use self::decoder::DC_API_CORE_INSTRUCTION_INPUT_CONTRACT_VERSION;
|
||||
/// Exhaustive published Metaplex account-key inventory.
|
||||
pub use self::decoder::DC_METADATA_MTM_METADATA_ACCOUNT_LAYOUT_AUDIT;
|
||||
/// Exposes the exact fixed byte length of one Buffer account header.
|
||||
pub use self::decoder::DC_METADATA_SPM_BUFFER_HEADER_BYTES;
|
||||
/// Exposes the exact byte length of one external-account data reference.
|
||||
pub use self::decoder::DC_METADATA_SPM_EXTERNAL_DATA_BYTES;
|
||||
/// Exposes the exact fixed byte length of one Metadata account header.
|
||||
pub use self::decoder::DC_METADATA_SPM_METADATA_HEADER_BYTES;
|
||||
/// Exposes the exact byte length of one Solana Program Metadata seed.
|
||||
pub use self::decoder::DC_METADATA_SPM_SEED_BYTES;
|
||||
/// Exact byte length of one SPL ElGamal registry account.
|
||||
pub use self::decoder::DC_SPL_ELGAMAL_REGISTRY_ACCOUNT_LEN;
|
||||
/// Reserved `kb-lib.decoder.adapter.saber_decimal_wrapper` decoder.
|
||||
@@ -226,6 +234,26 @@ pub use self::decoder::DcMetadataMtmTokenRecordAccountSnapshot;
|
||||
pub use self::decoder::DcMetadataSolanaProgramMetadataDecoder;
|
||||
/// Reserved `kb-lib.decoder.metadata.spl_name_service` decoder.
|
||||
pub use self::decoder::DcMetadataSplNameServiceDecoder;
|
||||
/// Solana Program Metadata account discriminator.
|
||||
pub use self::decoder::DcMetadataSpmAccountDiscriminator;
|
||||
/// Compression declared for Solana Program Metadata content.
|
||||
pub use self::decoder::DcMetadataSpmCompression;
|
||||
/// Typed Solana Program Metadata content without off-chain resolution.
|
||||
pub use self::decoder::DcMetadataSpmData;
|
||||
/// On-chain source used for Solana Program Metadata content.
|
||||
pub use self::decoder::DcMetadataSpmDataSource;
|
||||
/// Encoding declared for Solana Program Metadata content.
|
||||
pub use self::decoder::DcMetadataSpmEncoding;
|
||||
/// Fixed external-account data reference.
|
||||
pub use self::decoder::DcMetadataSpmExternalData;
|
||||
/// Format declared for Solana Program Metadata content.
|
||||
pub use self::decoder::DcMetadataSpmFormat;
|
||||
/// Bounded Solana Program Metadata model conversion error.
|
||||
pub use self::decoder::DcMetadataSpmModelError;
|
||||
/// Official Solana Program Metadata custom error.
|
||||
pub use self::decoder::DcMetadataSpmProgramError;
|
||||
/// Exact fixed-size Solana Program Metadata seed.
|
||||
pub use self::decoder::DcMetadataSpmSeed;
|
||||
/// Reserved `kb-lib.decoder.nft.metaplex_bubblegum` decoder.
|
||||
pub use self::decoder::DcNftMetaplexBubblegumDecoder;
|
||||
/// Exposes the reserved `DcNftTensorCnftDecoder` implementation.
|
||||
|
||||
Reference in New Issue
Block a user