Skip to content

Commit

Permalink
Index Token Inscriptions
Browse files Browse the repository at this point in the history
Program parser for inscriptions for blockbluster. Create and register
program parser with program tranformer. Add show_instrcitions flag to
the API to view the inscription information.
  • Loading branch information
Nagaprasadvr authored and kespinola committed Jan 7, 2025
1 parent 9654780 commit 497bd39
Show file tree
Hide file tree
Showing 27 changed files with 498 additions and 35 deletions.
3 changes: 3 additions & 0 deletions blockbuster/src/programs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ use bubblegum::BubblegumInstruction;
use mpl_core_program::MplCoreAccountState;
use token_account::TokenProgramAccount;
use token_extensions::TokenExtensionsProgramAccount;
use token_inscriptions::TokenInscriptionAccount;
use token_metadata::TokenMetadataAccountState;

pub mod bubblegum;
pub mod mpl_core_program;
pub mod token_account;
pub mod token_extensions;
pub mod token_inscriptions;
pub mod token_metadata;

// Note: `ProgramParseResult` used to contain the following variants that have been deprecated and
Expand All @@ -30,5 +32,6 @@ pub enum ProgramParseResult<'a> {
TokenMetadata(&'a TokenMetadataAccountState),
TokenProgramAccount(&'a TokenProgramAccount),
TokenExtensionsProgramAccount(&'a TokenExtensionsProgramAccount),
TokenInscriptionAccount(&'a TokenInscriptionAccount),
Unknown,
}
141 changes: 141 additions & 0 deletions blockbuster/src/programs/token_inscriptions/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
use serde::{Deserialize, Serialize};
use solana_sdk::{pubkey::Pubkey, pubkeys};

use crate::{
error::BlockbusterError,
program_handler::{ParseResult, ProgramParser},
};

use super::ProgramParseResult;

pubkeys!(
inscription_program_id,
"inscokhJarcjaEs59QbQ7hYjrKz25LEPRfCbP8EmdUp"
);

pub struct TokenInscriptionParser;

#[derive(Debug, Serialize, Deserialize)]
pub struct InscriptionData {
pub authority: String,
pub root: String,
pub content: String,
pub encoding: String,
pub inscription_data: String,
pub order: u64,
pub size: u32,
pub validation_hash: Option<String>,
}

impl InscriptionData {
pub const BASE_SIZE: usize = 121;
pub const INSCRIPTION_ACC_DATA_DISC: [u8; 8] = [232, 120, 205, 47, 153, 239, 229, 224];

pub fn try_unpack_data(data: &[u8]) -> Result<Self, BlockbusterError> {
let acc_disc = &data[0..8];

if acc_disc != Self::INSCRIPTION_ACC_DATA_DISC {
return Err(BlockbusterError::InvalidAccountType);
}

if data.len() < Self::BASE_SIZE {
return Err(BlockbusterError::CustomDeserializationError(
"Inscription Data is too short".to_string(),
));
}

let authority = Pubkey::try_from(&data[8..40]).unwrap();
let mint = Pubkey::try_from(&data[40..72]).unwrap();
let inscription_data = Pubkey::try_from(&data[72..104]).unwrap();
let order = u64::from_le_bytes(data[104..112].try_into().unwrap());
let size = u32::from_le_bytes(data[112..116].try_into().unwrap());
let content_type_len = u32::from_le_bytes(data[116..120].try_into().unwrap()) as usize;
let content = String::from_utf8(data[120..120 + content_type_len].to_vec()).unwrap();
let encoding_len = u32::from_le_bytes(
data[120 + content_type_len..124 + content_type_len]
.try_into()
.unwrap(),
) as usize;

let encoding = String::from_utf8(
data[124 + content_type_len..124 + content_type_len + encoding_len].to_vec(),
)
.unwrap();

let validation_exists = u8::from_le_bytes(
data[124 + content_type_len + encoding_len..124 + content_type_len + encoding_len + 1]
.try_into()
.unwrap(),
);

let validation_hash = if validation_exists == 1 {
let validation_hash_len = u32::from_le_bytes(
data[124 + content_type_len + encoding_len + 1
..128 + content_type_len + encoding_len + 1]
.try_into()
.unwrap(),
) as usize;
Some(
String::from_utf8(
data[128 + content_type_len + encoding_len + 1
..128 + content_type_len + encoding_len + 1 + validation_hash_len]
.to_vec(),
)
.unwrap(),
)
} else {
None
};
Ok(InscriptionData {
authority: authority.to_string(),
root: mint.to_string(),
content,
encoding,
inscription_data: inscription_data.to_string(),
order,
size,
validation_hash,
})
}
}

pub struct TokenInscriptionAccount {
pub data: InscriptionData,
}

impl ParseResult for TokenInscriptionAccount {
fn result(&self) -> &Self
where
Self: Sized,
{
self
}
fn result_type(&self) -> ProgramParseResult {
ProgramParseResult::TokenInscriptionAccount(self)
}
}

impl ProgramParser for TokenInscriptionParser {
fn key(&self) -> Pubkey {
inscription_program_id()
}
fn key_match(&self, key: &Pubkey) -> bool {
key == &inscription_program_id()
}

fn handles_account_updates(&self) -> bool {
true
}

fn handles_instructions(&self) -> bool {
false
}

fn handle_account(
&self,
account_data: &[u8],
) -> Result<Box<(dyn ParseResult + 'static)>, BlockbusterError> {
let data = InscriptionData::try_unpack_data(account_data)?;
Ok(Box::new(TokenInscriptionAccount { data }))
}
}
3 changes: 3 additions & 0 deletions digital_asset_types/src/dao/full_asset.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
use crate::dao::{asset, asset_authority, asset_creators, asset_data, asset_grouping};

use super::asset_v1_account_attachments;

#[derive(Clone, Debug, PartialEq)]
pub struct FullAsset {
pub asset: asset::Model,
pub data: Option<asset_data::Model>,
pub authorities: Vec<asset_authority::Model>,
pub creators: Vec<asset_creators::Model>,
pub groups: Vec<asset_grouping::Model>,
pub inscription: Option<asset_v1_account_attachments::Model>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct AssetRelated {
Expand Down
2 changes: 2 additions & 0 deletions digital_asset_types/src/dao/generated/sea_orm_active_enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ pub enum V1AccountAttachments {
MasterEditionV1,
#[sea_orm(string_value = "master_edition_v2")]
MasterEditionV2,
#[sea_orm(string_value = "token_inscription")]
TokenInscription,
#[sea_orm(string_value = "unknown")]
Unknown,
}
Expand Down
Loading

0 comments on commit 497bd39

Please sign in to comment.