├── .gitignore ├── Cargo.toml ├── README.md └── src └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "metaplex_decoder" 3 | version = "0.1.0" 4 | edition = "2018" 5 | 6 | [lib] 7 | name = "metaplex_decoder" 8 | crate-type = ["cdylib"] 9 | 10 | [dependencies] 11 | bs58 = "0.4.0" 12 | solana-program = "1.7.11" 13 | metaplex-token-metadata = "0.0.1" 14 | serde_json = "1.0.67" 15 | serde = "1.0.130" 16 | 17 | [dependencies.pyo3] 18 | version = "0.14.5" 19 | features = ["extension-module"] -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Simple Metaplex Metadata Decoder 2 | 3 | Install the correct Python wheel for your Python version with `pip`: 4 | 5 | ```bash 6 | pip install metaplex_decoder-0.1.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl 7 | ``` 8 | 9 | Example Python usage: 10 | 11 | ```python 12 | from metaplex_decoder import * 13 | account_info = "..." # Base58 string 14 | metadata = deserialize_metadata(account_info) 15 | ``` 16 | 17 | ## Releases 18 | 19 | Various versions of the Many Linux Python wheel are available on the releases page. 20 | 21 | ## Build With Rust 22 | 23 | [Install Rust](https://www.rust-lang.org/tools/install). 24 | 25 | Use `virtualenv` to create a Python virtualenv environment and then activate it: 26 | 27 | ```bash 28 | virtualenv env 29 | source env/bin/activate 30 | ``` 31 | 32 | Install `maturin`: 33 | 34 | ```bash 35 | pip install maturin 36 | ``` 37 | 38 | For Linux build in docker with the ManyLinux image: 39 | 40 | ```bash 41 | docker run --rm -v $(pwd):/io konstin2/maturin build --release 42 | ``` 43 | 44 | For MacOSX: 45 | 46 | ```bash 47 | maturin build 48 | ``` 49 | 50 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | use metaplex_token_metadata::state::{Key, Metadata}; 2 | use pyo3::prelude::*; 3 | use serde::Serialize; 4 | use serde_json::json; 5 | use solana_program::borsh::try_from_slice_unchecked; 6 | 7 | #[derive(Debug, Serialize)] 8 | pub struct JSONCreator { 9 | pub address: String, 10 | pub verified: bool, 11 | // In percentages, NOT basis points ;) Watch out! 12 | pub share: u8, 13 | } 14 | 15 | #[pyfunction] 16 | fn deserialize_metadata(base58_string: String) -> PyResult { 17 | let decoded: Vec = bs58::decode(base58_string) 18 | .into_vec() 19 | .expect("Failed to decode base58 string"); 20 | 21 | let m: Metadata = try_from_slice_unchecked(&decoded).unwrap(); 22 | 23 | let creators = m 24 | .data 25 | .creators 26 | .unwrap() 27 | .iter() 28 | .map(|c| JSONCreator { 29 | address: c.address.to_string(), 30 | verified: c.verified, 31 | share: c.share, 32 | }) 33 | .collect::>(); 34 | 35 | let data = json!({ 36 | "name": m.data.name.to_string().trim_matches(char::from(0)), 37 | "symbol": m.data.symbol.to_string().trim_matches(char::from(0)), 38 | "seller_fee_basis_points": m.data.seller_fee_basis_points, 39 | "uri": m.data.uri.to_string().trim_matches(char::from(0)), 40 | "creators": [creators], 41 | }); 42 | 43 | let metadata = json!({ 44 | "key": key_to_string(m.key).trim_matches(char::from(0)), 45 | "update_authority": m.update_authority.to_string().trim_matches(char::from(0)), 46 | "mint": m.mint.to_string().trim_matches(char::from(0)), 47 | "data": data, 48 | "primary_sale_happened": m.primary_sale_happened, 49 | "is_mutable": m.is_mutable, 50 | "edition_nonce": m.edition_nonce, 51 | }); 52 | 53 | Ok(metadata.to_string()) 54 | } 55 | 56 | fn key_to_string(key: Key) -> String { 57 | match key { 58 | Key::Uninitialized => "Uninitialized".to_string(), 59 | Key::EditionV1 => "EditionV1".to_string(), 60 | Key::MasterEditionV1 => "MasterEditionV1".to_string(), 61 | Key::ReservationListV1 => "ReservationListV1".to_string(), 62 | Key::MetadataV1 => "MetadataV1".to_string(), 63 | Key::ReservationListV2 => "ReservationListV2".to_string(), 64 | Key::MasterEditionV2 => "MasterEditionV2".to_string(), 65 | Key::EditionMarker => "EditionMarker".to_string(), 66 | } 67 | } 68 | 69 | #[pymodule] 70 | fn metaplex_decoder(_py: Python, m: &PyModule) -> PyResult<()> { 71 | m.add_function(wrap_pyfunction!(deserialize_metadata, m)?)?; 72 | 73 | Ok(()) 74 | } 75 | --------------------------------------------------------------------------------